mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-15 16:15:49 +08:00
On receive, the struct sockaddr is zeroed and does not have an address family until recvfrom fills it in. SASIZE assumes that if there is no address family, it is INET. This means that for IPv6 UDP sockets, the sockaddr is truncated and the reply is forwarded to the wrong address. Instead, use sizeof(PROXYSOCKADDRTYPE).
222 lines
7.4 KiB
C
222 lines
7.4 KiB
C
/*
|
|
3APA3A simplest proxy server
|
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
|
|
please read License Agreement
|
|
*/
|
|
|
|
#include "proxy.h"
|
|
|
|
int socksend(struct clientparam *param, SOCKET sock, unsigned char * buf, int bufsize, int to){
|
|
int sent = 0;
|
|
int res;
|
|
struct pollfd fds;
|
|
|
|
fds.fd = sock;
|
|
fds.events = POLLOUT;
|
|
do {
|
|
if(conf.timetoexit) return 0;
|
|
/* try to send first; poll only when the socket would block */
|
|
res = param?param->srv->so._send(param->sostate, sock, (char *)buf + sent, bufsize - sent, 0) : so._send(so.state, sock, (char *)buf + sent, bufsize - sent, 0);
|
|
if(res > 0) {
|
|
sent += res;
|
|
continue;
|
|
}
|
|
if(res < 0 && errno != EAGAIN && errno != EINTR) break;
|
|
res = param?param->srv->so._poll(param->sostate, &fds, 1, to*1000):so._poll(so.state, &fds, 1, to*1000);
|
|
if(res < 0 && (errno == EAGAIN || errno == EINTR)) continue;
|
|
if(res < 1) break;
|
|
} while (sent < bufsize);
|
|
return sent;
|
|
}
|
|
|
|
|
|
int socksendto(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to){
|
|
int sent = 0;
|
|
int res;
|
|
struct pollfd fds;
|
|
|
|
fds.fd = sock;
|
|
do {
|
|
if(conf.timetoexit) return 0;
|
|
fds.events = POLLOUT;
|
|
res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to);
|
|
if(res < 0 && (errno == EAGAIN || errno == EINTR)) continue;
|
|
if(res < 1) break;
|
|
res = param?param->srv->so._sendto(param->sostate, sock, (char *)buf + sent, bufsize - sent, 0, sin, SASIZE(sin)):so._sendto(so.state, sock, (char *)buf + sent, bufsize - sent, 0, sin, SASIZE(sin));
|
|
if(res < 0) {
|
|
if(errno != EAGAIN && errno != EINTR) break;
|
|
continue;
|
|
}
|
|
sent += res;
|
|
} while (sent < bufsize);
|
|
return sent;
|
|
}
|
|
|
|
int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to){
|
|
struct pollfd fds;
|
|
SASIZETYPE sasize;
|
|
int res;
|
|
|
|
fds.fd = sock;
|
|
fds.events = POLLIN;
|
|
if(conf.timetoexit) return EOF;
|
|
res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to);
|
|
if (res<1) return 0;
|
|
sasize = sizeof(PROXYSOCKADDRTYPE);
|
|
do {
|
|
res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, &sasize):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, &sasize);
|
|
} while (res < 0 && (errno == EAGAIN || errno == EINTR));
|
|
return res;
|
|
}
|
|
|
|
int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){
|
|
int len;
|
|
|
|
if(!param->clibuf){
|
|
if(!(param->clibuf = malloc(SRVBUFSIZE))) return 0;
|
|
param->clibufsize = SRVBUFSIZE;
|
|
param->clioffset = param->cliinbuf = 0;
|
|
}
|
|
if(param->cliinbuf && param->clioffset < param->cliinbuf){
|
|
return (int)param->clibuf[param->clioffset++];
|
|
}
|
|
param->clioffset = param->cliinbuf = 0;
|
|
if ((len = sockrecvfrom(param, param->clisock, (struct sockaddr *)¶m->sincr, param->clibuf, param->clibufsize, timeosec*1000 + timeousec))<=0) return EOF;
|
|
param->cliinbuf = len;
|
|
param->clioffset = 1;
|
|
return (int)*param->clibuf;
|
|
}
|
|
|
|
/* Put bytes back in front of whatever the client has not been read yet, so a
|
|
service which has already taken a request off the socket can hand it to
|
|
another one, which reads it the way it reads anything else. */
|
|
int pushbackcli(struct clientparam * param, const unsigned char * data, int len){
|
|
unsigned left = 0;
|
|
unsigned need;
|
|
|
|
if(len <= 0) return 0;
|
|
if(param->clibuf) left = param->cliinbuf - param->clioffset;
|
|
need = (unsigned)len + left;
|
|
|
|
if(!param->clibuf){
|
|
if(!(param->clibuf = malloc(need > SRVBUFSIZE? need : SRVBUFSIZE))) return 1;
|
|
param->clibufsize = need > SRVBUFSIZE? need : SRVBUFSIZE;
|
|
}
|
|
else if(param->clibufsize < need){
|
|
unsigned char *nb = realloc(param->clibuf, need);
|
|
|
|
if(!nb) return 1;
|
|
param->clibuf = nb;
|
|
param->clibufsize = need;
|
|
}
|
|
if(left) memmove(param->clibuf + len, param->clibuf + param->clioffset, left);
|
|
memcpy(param->clibuf, data, (size_t)len);
|
|
param->clioffset = 0;
|
|
param->cliinbuf = need;
|
|
return 0;
|
|
}
|
|
|
|
unsigned long sockfillbuffcli(struct clientparam * param, unsigned long size, int timeosec){
|
|
int len;
|
|
|
|
if(!param->clibuf) return 0;
|
|
if(param->cliinbuf == param->clioffset){
|
|
param->cliinbuf = param->clioffset = 0;
|
|
}
|
|
else if(param->clioffset){
|
|
memmove(param->clibuf, param->clibuf + param->clioffset, param->cliinbuf - param->clioffset);
|
|
param->cliinbuf -= param->clioffset;
|
|
param->clioffset = 0;
|
|
}
|
|
if(size <= param->cliinbuf) return size;
|
|
size -= param->cliinbuf;
|
|
if((len = sockrecvfrom(param, param->clisock, (struct sockaddr *)¶m->sincr, param->clibuf + param->cliinbuf, (param->clibufsize - param->cliinbuf) < size? param->clibufsize - param->cliinbuf:size, timeosec*1000)) > 0){
|
|
param->cliinbuf += len;
|
|
}
|
|
return param->cliinbuf;
|
|
}
|
|
|
|
unsigned long sockfillbuffsrv(struct clientparam * param, unsigned long size, int timeosec){
|
|
int len;
|
|
|
|
if(!param->srvbuf) return 0;
|
|
if(param->srvinbuf == param->srvoffset){
|
|
param->srvinbuf = param->srvoffset = 0;
|
|
}
|
|
else if(param->srvoffset){
|
|
memmove(param->srvbuf, param->srvbuf + param->srvoffset, param->srvinbuf - param->srvoffset);
|
|
param->srvinbuf -= param->srvoffset;
|
|
param->srvoffset = 0;
|
|
}
|
|
if(size <= param->srvinbuf) return size;
|
|
size -= param->srvinbuf;
|
|
if((len = sockrecvfrom(param, param->remsock, (struct sockaddr *)¶m->sinsr, param->srvbuf + param->srvinbuf, (param->srvbufsize - param->srvinbuf) < size? param->srvbufsize - param->srvinbuf:size, timeosec*1000)) > 0){
|
|
param->srvinbuf += len;
|
|
param->nreads++;
|
|
param->statssrv64 += len;
|
|
}
|
|
return param->srvinbuf;
|
|
}
|
|
|
|
|
|
int sockgetcharsrv(struct clientparam * param, int timeosec, int timeousec){
|
|
int len;
|
|
int bufsize;
|
|
|
|
if(!param->srvbuf){
|
|
bufsize = SRVBUFSIZE;
|
|
if(param->ndatfilterssrv > 0 && bufsize < 32768) bufsize = 32768;
|
|
if(!(param->srvbuf = malloc(bufsize))) return 0;
|
|
param->srvbufsize = bufsize;
|
|
param->srvoffset = param->srvinbuf = 0;
|
|
|
|
}
|
|
if(param->srvinbuf && param->srvoffset < param->srvinbuf){
|
|
return (int)param->srvbuf[param->srvoffset++];
|
|
}
|
|
param->srvoffset = param->srvinbuf = 0;
|
|
if ((len = sockrecvfrom(param, param->remsock, (struct sockaddr *)¶m->sinsr, param->srvbuf, param->srvbufsize, timeosec*1000 + timeousec))<=0) return EOF;
|
|
param->srvinbuf = len;
|
|
param->srvoffset = 1;
|
|
param->nreads++;
|
|
param->statssrv64 += len;
|
|
return (int)*param->srvbuf;
|
|
}
|
|
|
|
int sockgetlinebuf(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, int delim, int to){
|
|
int c;
|
|
int i=0;
|
|
|
|
for(;;){
|
|
unsigned char *base = which? param->srvbuf : param->clibuf;
|
|
unsigned *offp = which? ¶m->srvoffset : ¶m->clioffset;
|
|
unsigned *inp = which? ¶m->srvinbuf : ¶m->cliinbuf;
|
|
|
|
/* drain already-buffered bytes in bulk instead of one call per byte */
|
|
if(base && *offp < *inp && i < bufsize){
|
|
int avail = (int)(*inp - *offp);
|
|
int n = (bufsize - i < avail)? bufsize - i : avail;
|
|
if(delim != EOF){
|
|
unsigned char *d = (unsigned char *)memchr(base + *offp, delim, n);
|
|
if(d) n = (int)(d - (base + *offp)) + 1;
|
|
/* caller may use param->srvbuf / param->clibuf as buf */
|
|
memmove(buf + i, base + *offp, n);
|
|
i += n; *offp += n;
|
|
if(d || i >= bufsize) return i;
|
|
}
|
|
else {
|
|
memmove(buf + i, base + *offp, n);
|
|
i += n; *offp += n;
|
|
if(i >= bufsize) return i;
|
|
}
|
|
}
|
|
if(i >= bufsize) return i;
|
|
/* buffer empty: one call forces a refill (with poll + accounting), then bulk-drain the rest */
|
|
if((c = which? sockgetcharsrv(param, to, 0) : sockgetcharcli(param, to, 0)) == EOF) return i;
|
|
buf[i++] = c;
|
|
if((delim != EOF && c == delim) || i >= bufsize) return i;
|
|
}
|
|
}
|
|
|