/* 3APA3A simplest proxy server (c) 2002-2026 by Vladimir Dubrovin please read License Agreement */ #include "proxy.h" #define RETURN(xxx) { param->res = xxx; goto CLEANRET; } #define CL_LOGINCMD 0 #define CL_PLAIN 1 #define CL_LOGIN 2 #define CAP_PLAIN 1 #define CAP_LOGIN 2 #ifdef WITHMAIN #define NOSTARTTLS 1 #else #define NOSTARTTLS param->srv->nostarttls #endif void * imappchild(struct clientparam* param) { int i=0, res, method=CL_LOGINCMD, caps=0; unsigned char buf[2048]; unsigned char srvbuf[1024]; unsigned char ibuf[2048]; unsigned char tag[64]; unsigned char ub[320]; unsigned char pb[320]; unsigned char *se, *cmd, *user, *pass, *p1, *p2; *tag = 0; i = sprintf((char *)buf, "* OK [CAPABILITY IMAP4rev1%s AUTH=PLAIN AUTH=LOGIN] IMAP4rev1 Proxy Ready\r\n", NOSTARTTLS?"":" STARTTLS"); if(socksend(param, param->clisock, buf, i, conf.timeouts[STRING_S])!=i) {RETURN (691);} for(;;){ i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); if(i < 4) {RETURN(692);} buf[i] = 0; if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; if (!(se=(unsigned char *)strchr((char *)buf, ' ')) || (se - buf) >= (int)(sizeof(tag) - 1)) {RETURN(692);} memcpy(tag, buf, se - buf); tag[se - buf] = 0; cmd = se + 1; if(!strncasecmp((char *)cmd, "LOGOUT", 6)){ socksend(param, param->clisock, (unsigned char *)"* BYE\r\n", 7, conf.timeouts[STRING_S]); sprintf((char *)buf, "%.60s OK LOGOUT completed\r\n", (char *)tag); socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); RETURN(0); } if(!strncasecmp((char *)cmd, "CAPABILITY", 10)){ i = sprintf((char *)buf, "* CAPABILITY IMAP4rev1%s AUTH=PLAIN AUTH=LOGIN\r\n", NOSTARTTLS?"":" STARTTLS"); socksend(param, param->clisock, buf, i, conf.timeouts[STRING_S]); sprintf((char *)buf, "%.60s OK CAPABILITY completed\r\n", (char *)tag); socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); continue; } #ifndef WITHMAIN if(!strncasecmp((char *)cmd, "STARTTLS", 8) && !param->srv->nostarttls){ sprintf((char *)buf, "%.60s OK Begin TLS negotiation\r\n", (char *)tag); if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);} param->clientstarttls = S_IMAPP; if(!param->srv->targetport) param->srv->targetport = htons(143); return (void *)tlsprchild; } #endif if(!strncasecmp((char *)cmd, "LOGIN ", 6)){ user = cmd + 6; if(*user == '"') { user++; if (!(se=(unsigned char *)strchr((char *)user, '"'))) {RETURN(693);} *se = 0; pass = se + 1; if(*pass == ' ') pass++; } else { if (!(se=(unsigned char *)strchr((char *)user, ' '))) {RETURN(693);} *se = 0; pass = se + 1; } if(strlen((char *)user) >= sizeof(ub) || strlen((char *)pass) >= sizeof(pb)) {RETURN(693);} strcpy((char *)ub, (char *)user); strcpy((char *)pb, (char *)pass); break; } if(!strncasecmp((char *)cmd, "AUTHENTICATE PLAIN", 18)){ method = CL_PLAIN; se = cmd + 18; if(*se == ' ') se++; else se = NULL; if(!se || !*se){ if(socksend(param, param->clisock, (unsigned char *)"+ \r\n", 4, conf.timeouts[STRING_S])!=4) {RETURN(698);} i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); if(i < 1) {RETURN(698);} buf[i] = 0; if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; se = buf; } i = de64(se, ibuf, (int)sizeof(ibuf) - 1); if(i < 3) {RETURN(693);} ibuf[i] = 0; p1 = (unsigned char *)memchr(ibuf, 0, i); if(!p1 || p1 == ibuf + i - 1) {RETURN(693);} p2 = (unsigned char *)memchr(p1 + 1, 0, i - (int)(p1 + 1 - ibuf)); if(p2){ user = p1 + 1; pass = p2 + 1; } else { user = ibuf; pass = p1 + 1; } if(strlen((char *)user) >= sizeof(ub) || strlen((char *)pass) >= sizeof(pb)) {RETURN(693);} strcpy((char *)ub, (char *)user); strcpy((char *)pb, (char *)pass); break; } if(!strncasecmp((char *)cmd, "AUTHENTICATE LOGIN", 18)){ method = CL_LOGIN; se = cmd + 18; if(*se == ' ') se++; else se = NULL; if(se && *se){ i = de64(se, ub, (int)sizeof(ub) - 1); if(i < 0) {RETURN(693);} ub[i] = 0; } else { if(socksend(param, param->clisock, (unsigned char *)"+ VXNlcm5hbWU6\r\n", 16, conf.timeouts[STRING_S])!=16) {RETURN(698);} i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); if(i < 1) {RETURN(698);} buf[i] = 0; if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; i = de64(buf, ub, (int)sizeof(ub) - 1); if(i < 0) {RETURN(693);} ub[i] = 0; } if(socksend(param, param->clisock, (unsigned char *)"+ UGFzc3dvcmQ6\r\n", 16, conf.timeouts[STRING_S])!=16) {RETURN(698);} i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); if(i < 1) {RETURN(698);} buf[i] = 0; if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; i = de64(buf, pb, (int)sizeof(pb) - 1); if(i < 0) {RETURN(693);} pb[i] = 0; break; } sprintf((char *)buf, "%.60s BAD need LOGIN first\r\n", (char *)tag); socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); } if(parseconnusername((char *)ub, param, 0, 143)){RETURN(694);} if(*pb && !param->password) param->password = (unsigned char *)strdup((char *)pb); param->operation = CONNECT; res = (*param->srv->authfunc)(param); if(res) {RETURN(res);} i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); if( i < 4 ) {RETURN(695);} srvbuf[i] = 0; if(strncasecmp((char *)srvbuf, "* OK", 4)){RETURN(696);} if((se = (unsigned char *)strstr((char *)srvbuf, "[CAPABILITY "))){ if(hascap(se, "AUTH=PLAIN")) caps |= CAP_PLAIN; if(hascap(se, "AUTH=LOGIN")) caps |= CAP_LOGIN; } else { if(socksend(param, param->remsock, (unsigned char *)"zz CAPABILITY\r\n", 15, conf.timeouts[STRING_S])!=15) {RETURN(697);} for(;;){ i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); if(i < 3) {RETURN(697);} srvbuf[i] = 0; if(!strncasecmp((char *)srvbuf, "* CAPABILITY", 12)){ if(hascap(srvbuf, "AUTH=PLAIN")) caps |= CAP_PLAIN; if(hascap(srvbuf, "AUTH=LOGIN")) caps |= CAP_LOGIN; continue; } if(!strncasecmp((char *)srvbuf, "zz ", 3)) break; } } if(method == CL_PLAIN && !(caps & CAP_PLAIN)) method = (caps & CAP_LOGIN)? CL_LOGIN : CL_LOGINCMD; else if(method == CL_LOGIN && !(caps & CAP_LOGIN)) method = (caps & CAP_PLAIN)? CL_PLAIN : CL_LOGINCMD; else if(method == CL_LOGINCMD && caps) method = (caps & CAP_PLAIN)? CL_PLAIN : CL_LOGIN; if(method == CL_PLAIN){ i = (int)strlen((char *)param->extusername); res = (int)strlen((char *)pb); if(i + res + 2 > (int)sizeof(ibuf)) {RETURN(693);} ibuf[0] = 0; memcpy(ibuf + 1, param->extusername, i); ibuf[i + 1] = 0; memcpy(ibuf + i + 2, pb, res); if(!en64(ibuf, srvbuf, i + res + 2, (int)sizeof(srvbuf))) {RETURN(693);} if( socksend(param, param->remsock, tag, (int)strlen((char *)tag), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)" AUTHENTICATE PLAIN ", 20, conf.timeouts[STRING_S])!= 20 || socksend(param, param->remsock, srvbuf, (int)strlen((char *)srvbuf), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) {RETURN(699);} } else if(method == CL_LOGIN){ if( socksend(param, param->remsock, tag, (int)strlen((char *)tag), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)" AUTHENTICATE LOGIN\r\n", 21, conf.timeouts[STRING_S])!= 21) {RETURN(699);} i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); if(i < 1 || *srvbuf != '+') {RETURN(699);} if(!en64(param->extusername, ibuf, (int)strlen((char *)param->extusername), (int)sizeof(ibuf))) {RETURN(693);} if( socksend(param, param->remsock, ibuf, (int)strlen((char *)ibuf), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) {RETURN(699);} i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); if(i < 1 || *srvbuf != '+') {RETURN(699);} if(!en64(pb, ibuf, (int)strlen((char *)pb), (int)sizeof(ibuf))) {RETURN(693);} if( socksend(param, param->remsock, ibuf, (int)strlen((char *)ibuf), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) {RETURN(699);} } else { if( socksend(param, param->remsock, tag, (int)strlen((char *)tag), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)" LOGIN ", 7, conf.timeouts[STRING_S])!= 7 || socksend(param, param->remsock, param->extusername, (int)strlen((char *)param->extusername), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)" ", 1, conf.timeouts[STRING_S])!= 1 || socksend(param, param->remsock, pb, (int)strlen((char *)pb), conf.timeouts[STRING_S]) <= 0 || socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) {RETURN(699);} } param->statscli64 += (uint64_t)(strlen((char *)tag) + strlen((char *)param->extusername) + strlen((char *)pb) + 11); param->nwrites++; RETURN (mapsocket(param, 180)); CLEANRET: if(param->hostname&¶m->extusername) { sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, (*SAPORT(¶m->sinsr)==143)?0:':', ntohs(*SAPORT(¶m->sinsr))); dolog(param, buf); } else dolog(param, NULL); if(param->clisock != INVALID_SOCKET) { if ((param->res > 0 && param->res < 100) || (param->res > 691 && param->res <700)) { sprintf((char *)buf, "%.60s NO proxy error\r\n", *tag? (char *)tag : "*"); socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]); } } return (NULL); } #ifdef WITHMAIN struct proxydef childdef = { imappchild, 143, 0, S_IMAPP, " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n" }; #include "proxymain.c" #endif