mirror of
https://github.com/3proxy/3proxy.git
synced 2026-08-26 09:55:48 +08:00
253 lines
9.9 KiB
C
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&¶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
|