mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-16 00:25:48 +08:00
pop3p, imapp and smtpp are built with MAILPROXY=true, and ftppr and the ftp:// scheme of the HTTP proxy with FTP=true; with CMake the switches are 3PROXY_USE_MAILPROXY and 3PROXY_USE_FTP. Neither is in a default build, and the standalone binaries follow what was built. A configuration naming one of them is still read either way. The three mail protocols amount to a STARTTLS negotiation now that mail is carried over TLS, so without a proxy of their own those names are tlspr speaking the protocol: the file holds a stand-in which sets the protocol for the connection and returns tlspr for the caller to run, which serves the service name and a parent chain alike and leaves conf.c and the redirect table untouched. FTP has no such fallback, so the service is known, answers nothing and logs the refusal. The Linux workflow builds both, so all of it is still compiled and run.
256 lines
7.0 KiB
C
256 lines
7.0 KiB
C
/*
|
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
|
|
please read License Agreement
|
|
|
|
*/
|
|
|
|
#include "proxy.h"
|
|
|
|
#ifdef WITH_FTP
|
|
|
|
/*
|
|
* Read one FTP server response, skipping continuation lines (lines whose
|
|
* 4th character is '-' per RFC 959). Returns the line length on success,
|
|
* 0/<0 on socket error/timeout. The final line's text is left in buf.
|
|
*/
|
|
static int ftpreadresponse(struct clientparam *param, char *buf, int buflen) {
|
|
int i;
|
|
while((i = sockgetlinebuf(param, SERVER, (unsigned char *)buf, buflen - 1, '\n',
|
|
conf.timeouts[STRING_L])) > 0
|
|
&& (i < 3 || !isnumber(*buf) || buf[3] == '-')) {}
|
|
return i;
|
|
}
|
|
|
|
int ftplogin(struct clientparam *param, char *nbuf, int *innbuf) {
|
|
char tbuf[256];
|
|
int i;
|
|
char *buf;
|
|
int len;
|
|
int res;
|
|
|
|
buf = nbuf?nbuf:tbuf;
|
|
len = nbuf?*innbuf:sizeof(tbuf);
|
|
|
|
if(innbuf)*innbuf = 0;
|
|
if(len < 140) return 707;
|
|
i = ftpreadresponse(param, buf, len);
|
|
if(i < 3) return 706;
|
|
buf[i] = 0;
|
|
if(atoi(buf)/100 != 2) {
|
|
*innbuf = i;
|
|
return 702;
|
|
}
|
|
sprintf(buf, "USER %.128s\r\n", param->extusername?param->extusername:(unsigned char *)"anonymous");
|
|
if((int)socksend(param, param->remsock, (unsigned char *)buf, (int)strlen(buf), conf.timeouts[STRING_S]) != (int)strlen(buf)){
|
|
return 703;
|
|
}
|
|
param->statscli64 += (int)strlen(buf);
|
|
param->nwrites++;
|
|
i = ftpreadresponse(param, buf, len);
|
|
if(i < 3) return 704;
|
|
buf[i] = 0;
|
|
res = atoi(buf)/100;
|
|
if(res == 3){
|
|
sprintf(buf, "PASS %.128s\r\n",
|
|
param->extusername?
|
|
(param->extpassword?
|
|
param->extpassword:(unsigned char *)"")
|
|
:(unsigned char *)"3proxy@");
|
|
res = (int)strlen(buf);
|
|
if((int)socksend(param, param->remsock, (unsigned char *)buf, res, conf.timeouts[STRING_S]) != (int)strlen(buf)){
|
|
return 705;
|
|
}
|
|
param->statscli64 += res;
|
|
param->nwrites++;
|
|
while((i = sockgetlinebuf(param, SERVER, (unsigned char *)buf, len - 1, '\n', conf.timeouts[STRING_L])) > 0){
|
|
buf[i] = 0;
|
|
res = (i>3 && buf[3] != '-')? atoi(buf)/100 : 0;
|
|
if(res || (nbuf && (len-i) > 256 && i > 3)) {
|
|
buf += i;
|
|
len -= i;
|
|
if(innbuf)*innbuf += i;
|
|
}
|
|
if(res) break;
|
|
}
|
|
if(i < 3) {
|
|
return 701;
|
|
}
|
|
}
|
|
if(res != 2) {
|
|
return 700;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ftpcd(struct clientparam *param, unsigned char* path, char *nbuf, int *innbuf){
|
|
char buf[1024];
|
|
int i;
|
|
int inbuf = 0;
|
|
|
|
sprintf(buf, "CWD %.512s\r\n", path);
|
|
if((int)socksend(param, param->remsock, (unsigned char *)buf, (int)strlen(buf), conf.timeouts[STRING_S]) != (int)strlen(buf)){
|
|
return 711;
|
|
}
|
|
param->statscli64 += (int)strlen(buf);
|
|
param->nwrites++;
|
|
while((i = sockgetlinebuf(param, SERVER, (unsigned char *)buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L])) > 0 && (i < 3 || !isnumber(*buf) || buf[3] == '-')){
|
|
if(nbuf && innbuf && inbuf + i < *innbuf && i > 6) {
|
|
memcpy(nbuf + inbuf, buf, i);
|
|
inbuf += i;
|
|
}
|
|
}
|
|
if(innbuf)*innbuf = inbuf;
|
|
if(i < 3) return 712;
|
|
buf[3] = 0;
|
|
if(buf[0] != '2') return 710;
|
|
return 0;
|
|
}
|
|
|
|
int ftpres(struct clientparam *param, unsigned char * buf, int l){
|
|
int i;
|
|
|
|
if (l < 16) return 755;
|
|
i = ftpreadresponse(param, (char *)buf, l);
|
|
buf[i] = 0;
|
|
if(i < 3) return 751;
|
|
if(buf[0] != '2' && buf[0] != '1') return 750;
|
|
return 0;
|
|
}
|
|
|
|
int ftpsyst(struct clientparam *param, unsigned char *buf, unsigned len){
|
|
int i;
|
|
|
|
if(socksend(param, param->remsock, (unsigned char *)"SYST\r\n", 6, conf.timeouts[STRING_S]) != 6){
|
|
return 721;
|
|
}
|
|
param->statscli64 += 6;
|
|
param->nwrites++;
|
|
i = ftpreadresponse(param, (char *)buf, len);
|
|
if(i < 7) return 722;
|
|
buf[3] = 0;
|
|
if(atoi((char *)buf)/100 != 2) return 723;
|
|
buf[i-2] = 0;
|
|
memmove((char *)buf, (char *)buf+4, strlen((char *)buf+4)+1);
|
|
return 0;
|
|
}
|
|
|
|
int ftppwd(struct clientparam *param, unsigned char *buf, unsigned len){
|
|
int i;
|
|
char *b, *e;
|
|
|
|
if(socksend(param, param->remsock, (unsigned char *)"PWD\r\n", 5, conf.timeouts[STRING_S]) != 5){
|
|
return 731;
|
|
}
|
|
param->statscli64 += 5;
|
|
param->nwrites++;
|
|
i = ftpreadresponse(param, (char *)buf, len);
|
|
if(i < 7) return 732;
|
|
buf[3] = 0;
|
|
if(atoi((char *)buf)/100 != 2) return 733;
|
|
buf[i-2] = 0;
|
|
b = (char *)buf+4;
|
|
if(*b == '\"' && (e = strchr(b+1, '\"'))){
|
|
b++;
|
|
*e = 0;
|
|
}
|
|
memmove((char *)buf, b, strlen(b)+1);
|
|
return 0;
|
|
}
|
|
|
|
int ftptype(struct clientparam *param, unsigned char* f_type){
|
|
char buf[1024];
|
|
int i;
|
|
|
|
sprintf(buf, "TYPE %.512s\r\n", f_type);
|
|
if((int)socksend(param, param->remsock, (unsigned char *)buf, (int)strlen(buf), conf.timeouts[STRING_S]) != (int)strlen(buf)){
|
|
return 741;
|
|
}
|
|
param->statscli64 += (int)strlen(buf);
|
|
param->nwrites++;
|
|
i = ftpreadresponse(param, buf, sizeof(buf));
|
|
if(i < 3) return 742;
|
|
if(buf[0] != '2') return 740;
|
|
return 0;
|
|
}
|
|
|
|
|
|
SOCKET ftpdata(struct clientparam *param){
|
|
char buf[1024];
|
|
int i;
|
|
char *sb, *se;
|
|
SOCKET s = INVALID_SOCKET, rem;
|
|
unsigned long b1, b2, b3, b4;
|
|
unsigned short b5, b6;
|
|
SASIZETYPE sasize;
|
|
|
|
if(socksend(param, param->remsock, (unsigned char *)"PASV\r\n", 6, conf.timeouts[STRING_S]) != 6){
|
|
return INVALID_SOCKET;
|
|
}
|
|
param->statscli64 += 6;
|
|
param->nwrites++;
|
|
i = ftpreadresponse(param, buf, sizeof(buf));
|
|
if(i < 7) return INVALID_SOCKET;
|
|
if(buf[0] != '2') return INVALID_SOCKET;
|
|
buf[i-2] = 0;
|
|
if(!(sb = strchr(buf+4, '(')) || !(se= strchr(sb, ')'))) return INVALID_SOCKET;
|
|
if(sscanf(sb+1, "%lu,%lu,%lu,%lu,%hu,%hu", &b1, &b2, &b3, &b4, &b5, &b6)!=6) return INVALID_SOCKET;
|
|
sasize = sizeof(param->sinsl);
|
|
if(param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsl, &sasize)){return INVALID_SOCKET;}
|
|
sasize = sizeof(param->sinsr);
|
|
if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsr, &sasize)){return INVALID_SOCKET;}
|
|
rem = param->remsock;
|
|
param->remsock = INVALID_SOCKET;
|
|
param->req = param->sinsr;
|
|
*SAPORT(¶m->req) = *SAPORT(¶m->sinsr) = htons((uint16_t)(((b5 & 0xff)<<8) | (b6 & 0xff)));
|
|
*SAPORT(¶m->sinsl) = 0;
|
|
i = param->operation;
|
|
param->operation = FTP_DATA;
|
|
if((param->res = (*param->srv->authfunc)(param))) {
|
|
if(param->remsock != INVALID_SOCKET) {
|
|
param->srv->so._closesocket(param->sostate, param->remsock);
|
|
param->remsock = INVALID_SOCKET;
|
|
}
|
|
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
|
if((param->res = (*param->srv->authfunc)(param))) {
|
|
param->remsock = rem;
|
|
return INVALID_SOCKET;
|
|
}
|
|
}
|
|
param->operation = i;
|
|
s = param->remsock;
|
|
param->remsock = rem;
|
|
return s;
|
|
}
|
|
|
|
SOCKET ftpcommand(struct clientparam *param, unsigned char * command, unsigned char *arg) {
|
|
char buf[1024];
|
|
int i;
|
|
SOCKET s;
|
|
|
|
|
|
s = ftpdata(param);
|
|
if(s==INVALID_SOCKET) return INVALID_SOCKET;
|
|
sprintf(buf, "%.15s%s%.512s\r\n", command, arg?
|
|
(unsigned char *)" ":(unsigned char *)"",
|
|
arg?arg:(unsigned char *)"");
|
|
if((int)socksend(param, param->remsock, (unsigned char *)buf, (int)strlen(buf), conf.timeouts[STRING_S]) != (int)strlen(buf)){
|
|
param->srv->so._closesocket(param->sostate, s);
|
|
return INVALID_SOCKET;
|
|
}
|
|
param->statscli64 += (int)strlen(buf);
|
|
param->nwrites++;
|
|
i = ftpreadresponse(param, buf, sizeof(buf));
|
|
if(i < 3) {
|
|
param->srv->so._closesocket(param->sostate, s);
|
|
return INVALID_SOCKET;
|
|
}
|
|
if(buf[0] != '1') {
|
|
param->srv->so._closesocket(param->sostate, s);
|
|
return INVALID_SOCKET;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
#endif
|