3proxy/src/imapp.c
2026-08-08 17:58:49 +03:00

253 lines
9.9 KiB
C

/*
3APA3A simplest proxy server
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
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&&param->extusername) {
sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, (*SAPORT(&param->sinsr)==143)?0:':', ntohs(*SAPORT(&param->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