mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-28 22:05:48 +08:00
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
A server which logs in on USER alone (230, anonymous FTP often does) or refuses it had its answer read but not handed back: ftplogin() gave the length of what it read only for the answer to PASS. The client, told 331 by ftppr and waiting for the answer to its PASS, waited until the time out. The answer to USER is the client's now when there is no PASS. The test case logs in through ftppr to a server answering 230, a multiline 230, 331 then 230, 530, and 331 then 530. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
256 lines
7.2 KiB
C
256 lines
7.2 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',
|
|
param->srv->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) {
|
|
if(innbuf)*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), param->srv->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 : (unsigned char *)"3proxy@");
|
|
res = (int)strlen(buf);
|
|
if((int)socksend(param, param->remsock, (unsigned char *)buf, res, param->srv->timeouts[STRING_S]) != (int)strlen(buf)){
|
|
return 705;
|
|
}
|
|
param->statscli64 += res;
|
|
param->nwrites++;
|
|
while((i = sockgetlinebuf(param, SERVER, (unsigned char *)buf, len - 1, '\n', param->srv->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;
|
|
}
|
|
}
|
|
/* USER answered at once, logged in (230) or refused: that answer is
|
|
the client's, waiting for one to its PASS */
|
|
else if(innbuf)*innbuf = i;
|
|
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), param->srv->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', param->srv->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, param->srv->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, param->srv->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), param->srv->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, param->srv->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 = clientauth(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 = clientauth(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), param->srv->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
|