/* 3APA3A simplest proxy server (c) 2002-2026 by Vladimir Dubrovin please read License Agreement */ #include "proxy.h" #define RETURN(xxx) { param->res = xxx; goto CLEANRET; } char * proxy_stringtable[] = { /* 0 */ "HTTP/1.0 400 Bad Request\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "400 Bad Request\r\n" "

400 Bad Request

\r\n", /* 1 */ "HTTP/1.0 502 Bad Gateway\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "502 Bad Gateway\r\n" "

502 Bad Gateway

Host Not Found or connection failed

\r\n", /* 2 */ "HTTP/1.0 503 Service Unavailable\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "503 Service Unavailable\r\n" "

503 Service Unavailable

You have exceeded your limits

\r\n", /* 3 */ "HTTP/1.0 503 Service Unavailable\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "503 Service Unavailable\r\n" "

503 Service Unavailable

Recursion detected

\r\n", /* 4 */ "HTTP/1.0 501 Not Implemented\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "501 Not Implemented\r\n" "

501 Not Implemented

Required action is not supported by proxy server

\r\n", /* 5 */ "HTTP/1.0 502 Bad Gateway\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "502 Bad Gateway\r\n" "

502 Bad Gateway

Failed to connect parent proxy

\r\n", /* 6 */ "HTTP/1.0 500 Internal Error\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "500 Internal Error\r\n" "

500 Internal Error

Internal proxy error during processing your request

\r\n", /* 7 */ "HTTP/1.0 407 Proxy Authentication Required\r\n" "Proxy-Authenticate: Basic realm=\"proxy\"\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "407 Proxy Authentication Required\r\n" "

407 Proxy Authentication Required

Access to requested resource disallowed by administrator or you need valid username/password to use this resource

\r\n", /* 8 */ "HTTP/1.0 200 Connection established\r\n\r\n", /* 9 */ "HTTP/1.0 200 Connection established\r\n" "Content-Type: text/html\r\n\r\n", /* 10*/ "HTTP/1.0 404 Not Found\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "404 Not Found\r\n" "

404 Not Found

File not found\r\n", /* 11*/ "HTTP/1.0 403 Forbidden\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "403 Access Denied\r\n" "

403 Access Denied

Access control list denies you to access this resource\r\n", /* 12*/ "HTTP/1.0 407 Proxy Authentication Required\r\n" #ifndef NOCRYPT "Proxy-Authenticate: NTLM\r\n" #endif "Proxy-Authenticate: Basic realm=\"proxy\"\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "407 Proxy Authentication Required\r\n" "

407 Proxy Authentication Required

Access to requested resource disallowed by administrator or you need valid username/password to use this resource

\r\n", /* 13*/ "HTTP/1.0 407 Proxy Authentication Required\r\n" "Connection: keep-alive\r\n" "Content-Length: 0\r\n" "Proxy-Authenticate: NTLM ", /* 14*/ "HTTP/1.0 403 Forbidden\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "
",

/* 15*/	"HTTP/1.0 503 Service Unavailable\r\n"
	"Connection: close\r\n"
	"Content-type: text/html; charset=utf-8\r\n"
	"\r\n"
	"503 Service Unavailable\r\n"
	"

503 Service Unavailable

Your request violates configured policy

\r\n", /* 16*/ "HTTP/1.0 401 Authentication Required\r\n" "WWW-Authenticate: Basic realm=\"FTP Server\"\r\n" "Connection: close\r\n" "Content-type: text/html; charset=utf-8\r\n" "\r\n" "401 FTP Server requires authentication\r\n" "

401 FTP Server requires authentication

This FTP server rejects anonymous access

\r\n", /* 17*/ "HTTP/1.1 100 Continue\r\n" "\r\n", NULL }; #define LINESIZE 32768 /* "Content-Length: " plus 20 digits plus CRLF and a NUL, rounded up */ #define CLHDRSIZE 48 /* what the headers this proxy adds of its own can come to: a Forwarded or Via with a host name in it, a Connection, a Proxy-support and a Proxy-Authorization carrying an encoded user and password */ #define HDRRESERVE 2048 #define BUFSIZE (LINESIZE*2) #define FTPBUFSIZE 1536 #define PST_COUNT (sizeof(proxy_stringtable)/sizeof(proxy_stringtable[0]) - 1) static int proxy_stringtable_len[PST_COUNT]; static int pst_len(int idx){ int len = proxy_stringtable_len[idx]; if(!len) { len = (int)strlen(proxy_stringtable[idx]); proxy_stringtable_len[idx] = len; } return len; } static int send_st(struct clientparam *param, int idx){ return socksend(param, param->clisock, (unsigned char *)proxy_stringtable[idx], pst_len(idx), conf.timeouts[STRING_S]); } /* Makes room in a buffer whose size is tracked. A filter may hand back one holding exactly what it produced, so nothing may be added to it without asking for the room first. Returns 1 when the room cannot be had. */ static int growbuf(unsigned char **buf, int *bufsize, int need){ unsigned char *newbuf; if(need <= *bufsize) return 0; need += BUFSIZE; /* for what follows too, not just this */ if(!(newbuf = realloc(*buf, need))) return 1; *buf = newbuf; *bufsize = need; return 0; } static void freeptr(void *p){ void **pp = (void **)p; if(*pp) { free(*pp); *pp = NULL; } } #ifndef WITHMAIN /* Point at the path in a request line and report the authority it names. Returns NULL if the line is not one we can put back together. */ static unsigned char * reqpath(unsigned char *line, unsigned char **host, int *hostlen) { unsigned char *sp, *p; *host = NULL; *hostlen = 0; if(!line || !(sp = (unsigned char *)strchr((char *)line, ' '))) return NULL; while(*sp == ' ') sp++; if(*sp == '/') return sp; if(strncasecmp((char *)sp, "http://", 7)) return NULL; *host = p = sp + 7; while(*p && *p != '/' && *p != ' ') p++; *hostlen = (int)(p - *host); return (*p == '/')? p : NULL; } #endif static void logurl(struct clientparam * param, char * buf, char * req, int ftp){ char *sb; char *se; int len; if(!buf) req = NULL; if(req) { len = (int)strlen(req); if(len > (LINESIZE - 1)) len = LINESIZE - 1; memcpy(buf, req, len + 1); buf[LINESIZE - 1] = 0; sb = strchr(buf, '\r'); if(sb)*sb = 0; if(ftp && (se = strchr(buf + 10, ':')) && (sb = strchr(se, '@')) ) { memmove(se, sb, strlen(sb)+1); } } if(param->res != 555 && param->res != 508)dolog(param, (unsigned char *)(req?buf:NULL)); } void decodeurl(unsigned char *s, int allowcr){ unsigned char *d = s; unsigned u; while(*s){ if(*s == '%' && ishex(s[1]) && ishex(s[2])){ sscanf((char *)s+1, "%2x", &u); if(allowcr && u != '\r')*d++ = u; else if (u != '\r' && u != '\n') { if (u == '\"' || u == '\\') *d++ = '\\'; else if (u == 255) *d++ = 255; *d++ = u; } s+=3; } else if(!allowcr && *s == '?') { break; } else if(*s == '+') { *d++ = ' '; s++; } else { *d++ = *s++; } } *d = 0; } void file2url(unsigned char *sb, unsigned char *buf, unsigned bufsize, int * inbuf, int skip255){ for(; *sb; sb++){ if((bufsize - *inbuf)<16)break; if(*sb=='\r'||*sb=='\n')continue; if(isallowed(*sb))buf[(*inbuf)++]=*sb; else if(*sb == '\"'){ memcpy(buf+*inbuf, "%5C%22", 6); (*inbuf)+=6; } else if(skip255 && *sb == 255 && *(sb+1) == 255) { memcpy(buf+*inbuf, "%ff", 3); (*inbuf)+=3; sb++; } else { sprintf((char *)buf+*inbuf, "%%%.2x", (unsigned)*sb); (*inbuf)+=3; } } } void * proxychild(struct clientparam* param) { int res=0, i=0; unsigned char* buf = NULL, *newbuf; int inbuf; int bufsize; unsigned reqlen = 0; unsigned char *sb=NULL, *sg=NULL, *se=NULL, *sp=NULL, *req=NULL, *su=NULL, *ss = NULL; unsigned char *ftpbase=NULL; unsigned char username[1024]; int keepalive = 0; uint64_t contentlength64 = 0; int hascontent =0; int clhdrofs = -1; int chunkedcli = 0; int clhdrofs_req = -1; int isconnect = 0; int redirect = 0; int prefix = 0, ckeepalive=0; int ftp = 0; int ssoff = 0; #ifndef ANONYMOUS int anonymous; #endif int sleeptime = 0; #ifndef WITHMAIN int reqsize, reqbufsize; unsigned char *origreq = NULL; #endif int authenticate; struct pollfd fds[2]; SOCKET ftps; char ftpbuf[FTPBUFSIZE]; int inftpbuf = 0; int haveconnection = 0; #ifndef WITHMAIN FILTER_ACTION action; #endif if(param->remsock != INVALID_SOCKET) haveconnection = 1; if(!(buf = malloc(BUFSIZE))) {RETURN(21);} bufsize = BUFSIZE; #ifndef ANONYMOUS anonymous = param->srv->anonymous; #endif for(;;){ memset(buf, 0, bufsize); inbuf = 0; chunkedcli = 0; clhdrofs_req = -1; param->nolongdatfilter = 0; if(keepalive && (param->cliinbuf == param->clioffset) && (param->remsock != INVALID_SOCKET)){ memset(fds, 0, sizeof(fds)); fds[0].fd = param->clisock; fds[0].events = POLLIN; fds[1].fd = param->remsock; fds[1].events = POLLIN; res = param->srv->so._poll(param->sostate, fds, 2, conf.timeouts[STRING_S]*1000); if(res<=0) { RETURN(555); } if((fds[1].revents & (POLLIN|POLLHUP|POLLERR|POLLNVAL))) { if(param->transparent || (!param->redirected && param->redirtype == R_HTTP)) RETURN(555); ckeepalive = 0; param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR); param->srv->so._closesocket(param->sostate, param->remsock); param->remsock = INVALID_SOCKET; param->redirected = 0; param->redirtype = 0; memset(¶m->sinsl, 0, sizeof(param->sinsl)); memset(¶m->sinsr, 0, sizeof(param->sinsr)); memset(¶m->req, 0, sizeof(param->req)); } } i = sockgetlinebuf(param, CLIENT, buf, LINESIZE - 1, '\n', conf.timeouts[STRING_L]); if(i<=0) { RETURN((keepalive)?555:(i)?507:508); } if (i==2 && buf[0]=='\r' && buf[1]=='\n') continue; buf[i] = 0; if(req) { if(!param->transparent && !param->srv->transparent && (i<=prefix || strncasecmp((char *)buf, (char *)req, prefix))){ ckeepalive = 0; if(param->remsock != INVALID_SOCKET){ param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR); param->srv->so._closesocket(param->sostate, param->remsock); } param->remsock = INVALID_SOCKET; param->redirected = 0; param->redirtype = 0; memset(¶m->sinsl, 0, sizeof(param->sinsl)); memset(¶m->sinsr, 0, sizeof(param->sinsr)); memset(¶m->req, 0, sizeof(param->req)); } free(req); } req = (unsigned char *)strdup((char *)buf); if(!req){RETURN(510);} if(i<10) { RETURN(511); } if(buf[i-3] == '1') keepalive = 2; param->transparent = 0; if((isconnect = !strncasecmp((char *)buf, "CONNECT", 7))) keepalive = 2; if ((sb=(unsigned char *)(unsigned char *)strchr((char *)buf, ' ')) == NULL) {RETURN(512);} ss = ++sb; ssoff = (int)(ss - buf); if(!isconnect) { if (!strncasecmp((char *)sb, "http://", 7)) { sb += 7; } else if (!strncasecmp((char *)sb, "ftp://", 6)) { ftp = 1; sb += 6; } else if(*sb == '/') { param->transparent = 1; } else { RETURN (513); } } else { if ((se=(unsigned char *)(unsigned char *)strchr((char *)sb, ' ')) == NULL || sb==se) {RETURN (514);} *se = 0; } if(!param->transparent || isconnect) { if(!isconnect) { if ((se=(unsigned char *)(unsigned char *)strchr((char *)sb, '/')) == NULL || sb==se || !(sg=(unsigned char *)strchr((char *)sb, ' '))) {RETURN (515);} if(se > sg) se=sg; *se = 0; } prefix = (int)(se - buf); su = (unsigned char*)strrchr((char *)sb, '@'); if(su) { su = (unsigned char *)strdup((char *)sb); decodeurl(su, 0); if(parseconnusername((char *)su, (struct clientparam *)param, 1, (uint16_t)((ftp)?21:80))) { free(su); RETURN (100); } free(su); } else if(parsehostname((char *)sb, (struct clientparam *)param, (uint16_t)((ftp)? 21:80))) RETURN(100); if(!isconnect){ if(se==sg)*se-- = ' '; *se = '/'; memmove(ss, se, i - (se - buf) + 1); } } reqlen = i = (int)strlen((char *)buf); { static const struct { const char *m; int mlen; int op_http, op_ftp; } methods[] = { {"CONNECT", 7, HTTP_CONNECT, HTTP_CONNECT}, {"GET", 3, HTTP_GET, FTP_GET}, {"PUT", 3, HTTP_PUT, FTP_PUT}, {"POST", 4, HTTP_POST, HTTP_POST}, {"BITS_POST", 9, HTTP_POST, HTTP_POST}, {"HEAD", 4, HTTP_HEAD, HTTP_HEAD}, }; int k; param->operation = HTTP_OTHER; for(k = 0; k < (int)(sizeof(methods)/sizeof(methods[0])); k++){ if(!strncasecmp((char *)buf, methods[k].m, methods[k].mlen)){ param->operation = ftp ? methods[k].op_ftp : methods[k].op_http; break; } } } do { buf[inbuf+i]=0; /*printf("Got: %s\n", buf+inbuf);*/ #ifndef WITHMAIN if(i > 25 && !param->srv->transparent && (!strncasecmp((char *)(buf+inbuf), "proxy-authorization", 19))){ sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; if(!*sb) continue; if(!strncasecmp((char *)sb, "basic", 5)){ sb+=5; while(isspace(*sb))sb++; i = de64(sb, username, 255); if(i<=0)continue; username[i] = 0; sb = (unsigned char *)strchr((char *)username, ':'); if(sb){ *sb = 0; if(param->password)free(param->password); param->password = (unsigned char *)strdup((char *)sb+1); param->pwtype = 0; } else if(param->password){ free(param->password); param->password = NULL; } if(param->username)free(param->username); param->username = (unsigned char *)strdup((char *)username); continue; } } #endif if(!isconnect && ( (i> 25 && !strncasecmp((char *)(buf+inbuf), "proxy-connection:", 17)) || (i> 16 && (!strncasecmp((char *)(buf+inbuf), "connection:", 11))) )){ sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; if(strncasecmp((char *)sb,"upgrade", 7)){ if(!strncasecmp((char *)sb,"keep-alive", 10))keepalive = 1; else keepalive = 0; continue; } } if( i > 11 && !strncasecmp((char *)(buf+inbuf), "Expect: 100", 11)){ keepalive = 1; send_st(param, 17); continue; } if(param->transparent && i > 6 && !strncasecmp((char *)buf + inbuf, "Host:", 5)){ unsigned char c; sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; (se = (unsigned char *)strchr((char *)sb, '\r')) || (se = (unsigned char *)strchr((char *)sb, '\n')); if(se) { c = *se; *se = 0; } if(param->hostname && (!*param->hostname || isnumber(param->hostname[strlen((char *)param->hostname) - 1]))){ free(param->hostname); param->hostname = NULL; } if(!param->hostname){ if(parsehostname((char *)sb, param, 80)) RETURN(100); } newbuf = malloc(strlen((char *)req) + strlen((char *)(buf+inbuf)) + 8); sp = (unsigned char *)strchr((char *)req+1, '/'); if(newbuf && sp){ memcpy(newbuf, req, (sp - req)); sprintf((char*)newbuf + (sp - req), "http://%s%s",sb,sp); free(req); req = newbuf; } else if(newbuf) free(newbuf); if(se)*se = c; } if(ftp && i > 13 && (!strncasecmp((char *)(buf+inbuf), "authorization", 13))){ sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; if(!*sb) continue; if(!strncasecmp((char *)sb, "basic", 5)){ sb+=5; while(isspace(*sb))sb++; i = de64(sb, username, 255); if(i<=0)continue; username[i] = 0; sb = (unsigned char *)strchr((char *)username, ':'); if(sb){ *sb = 0; if(param->extpassword)free(param->extpassword); param->extpassword = (unsigned char *)strdup((char *)sb+1); } else if(param->extpassword){ free(param->extpassword); param->extpassword = NULL; } if(param->extusername)free(param->extusername); param->extusername = (unsigned char *)strdup((char *)username); continue; } } if(i> 15 && (!strncasecmp((char *)(buf+inbuf), "content-length", 14))){ if(chunkedcli) continue; sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; sscanf((char *)sb, "%"SCNu64"",&contentlength64); if(param->maxtrafout64 && (param->maxtrafout64 < param->statscli64 || contentlength64 > param->maxtrafout64 - param->statscli64)){ RETURN(10); } if(param->ndatfilterscli > 0 && contentlength64 > 0) continue; clhdrofs_req = inbuf; } else if(i>25 && (!strncasecmp((char *)(buf+inbuf), "transfer-encoding", 17))){ sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(sb){ ++sb; while(isspace(*sb))sb++; if(!strncasecmp((char *)sb, "chunked", 7)){ chunkedcli = 1; if(clhdrofs_req >= 0){ buf[clhdrofs_req] = 'X'; clhdrofs_req = -1; } contentlength64 = 0; param->nolongdatfilter = 1; } } } inbuf += i; if((bufsize - inbuf) < LINESIZE){ if (bufsize > (LINESIZE * 16)){ RETURN (516); } if(!(newbuf = realloc(buf, bufsize + BUFSIZE))){RETURN (21);} buf = newbuf; bufsize += BUFSIZE; } } while( (i = sockgetlinebuf(param, CLIENT, buf + inbuf, LINESIZE - 2, '\n', conf.timeouts[STRING_S])) > 2); buf[inbuf] = 0; #ifndef WITHMAIN reqsize = (int)strlen((char *)req); reqbufsize = reqsize + 1; #endif if(param->srv->needuser > 1 && !param->username) {RETURN(4);} if((res = (*param->srv->authfunc)(param))) { if (res <= 10 || haveconnection || param->transparent) RETURN(res); param->srv->so._closesocket(param->sostate, param->remsock); param->remsock = INVALID_SOCKET; param->redirected = 0; param->redirtype = 0; memset(¶m->sinsl, 0, sizeof(param->sinsl)); memset(¶m->sinsr, 0, sizeof(param->sinsr)); if((res = (*param->srv->authfunc)(param))) RETURN(res); } #ifndef WITHMAIN /* Only worth keeping a copy when something can rewrite it. */ if(param->nreqfilters) origreq = (unsigned char *)strdup((char *)req); action = handlereqfilters(param, &req, &reqbufsize, 0, &reqsize); if(action == HANDLED){ freeptr(&origreq); RETURN(0); } if(action != PASS){ freeptr(&origreq); RETURN(517); } /* Only the copy in req was rewritten. On a direct connection the server is sent the request line held in buf, which was parsed and reduced to its path before the filters ran, so put the new path there as well. The destination was chosen, and the access rules applied to it, before the rewrite happened. A rewrite that changes the method or the authority is therefore left alone: acting on it would send the request somewhere the rules never saw. */ if(origreq && !isconnect && !ftp && strcmp((char *)req, (char *)origreq)){ unsigned char *oldhost, *newhost, *oldpath, *newpath; int oldhostlen, newhostlen, methodlen; methodlen = (int)(strchr((char *)origreq, ' ') - (char *)origreq); oldpath = reqpath(origreq, &oldhost, &oldhostlen); newpath = reqpath(req, &newhost, &newhostlen); if(oldpath && newpath && methodlen > 0 && !strncmp((char *)req, (char *)origreq, methodlen) && req[methodlen] == ' ' && oldhostlen == newhostlen && (!oldhostlen || !strncasecmp((char *)oldhost, (char *)newhost, oldhostlen))){ int newlen = (int)strlen((char *)newpath); int delta = newlen - ((int)reqlen - ssoff); if(ssoff > 0 && (int)reqlen >= ssoff && inbuf + delta < bufsize - 1){ memmove(buf + ssoff + newlen, buf + reqlen, inbuf - reqlen + 1); memcpy(buf + ssoff, newpath, newlen); inbuf += delta; reqlen += delta; buf[inbuf] = 0; } } } freeptr(&origreq); action = handlehdrfilterscli(param, &buf, &bufsize, 0, &inbuf); if(action == HANDLED){ RETURN(0); } if(action != PASS) RETURN(517); /* A filter may have returned a buffer sized to exactly what it produced. The headers this proxy adds of its own go in after it, so the room for them is taken back before anything is written. */ if(growbuf(&buf, &bufsize, inbuf + HDRRESERVE)) RETURN(21); param->nolongdatfilter = 0; #endif if(isconnect && param->redirtype != R_HTTP) { send_st(param, 8); } #ifndef WITHMAIN if (param->npredatfilters){ action = handlepredatflt(param); if(action == HANDLED){ RETURN(0); } if(action != PASS) RETURN(19); } if (conf.filtermaxsize && contentlength64 > (uint64_t)conf.filtermaxsize) { param->nolongdatfilter = 1; } else if(param->ndatfilterscli > 0 && contentlength64 > 0 && contentlength64 == (uint64_t)(unsigned long)contentlength64){ uint64_t newlen64; newlen64 = (uint64_t) sockfillbuffcli(param, (unsigned long)contentlength64, CONNECTION_S); if(newlen64 == contentlength64) { action = handledatfltcli(param, ¶m->clibuf, (int *)¶m->clibufsize, 0, (int *)¶m->cliinbuf); if(action == HANDLED){ RETURN(0); } if(action != PASS) RETURN(517); contentlength64 = param->cliinbuf; param->nolongdatfilter = 1; } if(growbuf(&buf, &bufsize, (int)strlen((char *)buf) + CLHDRSIZE)) RETURN(21); sprintf((char*)buf+strlen((char *)buf), "Content-Length: %"PRIu64"\r\n", contentlength64); } #endif if(ftp && param->redirtype != R_HTTP){ SOCKET s; int mode = 0; int i=0; inftpbuf = 0; if(!ckeepalive){ inftpbuf = FTPBUFSIZE - 20; res = ftplogin(param, ftpbuf, &inftpbuf); if(res){ RETURN(res); } } ckeepalive = 1; if(ftpbase) free(ftpbase); ftpbase = NULL; ss = buf + ssoff; if(!(sp = (unsigned char *)strchr((char *)ss, ' '))){RETURN(799);} *sp = 0; decodeurl(ss, 0); i = (int)strlen((char *)ss); if(!(ftpbase = malloc(i+2))){RETURN(21);} memcpy(ftpbase, ss, i); if(ftpbase[i-1] != '/') ftpbase[i++] = '/'; ftpbase[i] = 0; memcpy(buf, "

\n", 10); inbuf = 10; if(inftpbuf) { memcpy(buf+inbuf, ftpbuf, inftpbuf); inbuf += inftpbuf; memcpy(buf+inbuf, "
", 4); inbuf += 4; } if(ftpbase[1] != 0){ memcpy(buf+inbuf, "[..]\n", 22); inbuf += 22; } inftpbuf = FTPBUFSIZE - (20 + inftpbuf); res = ftpcd(param, ftpbase, ftpbuf, &inftpbuf); if(res){ res = ftptype(param, (unsigned char *)"I"); if(res)RETURN(res); ftpbase[--i] = 0; ftps = ftpcommand(param, param->operation == FTP_PUT? (unsigned char *)"STOR" : (unsigned char *)"RETR", ftpbase); } else { if(inftpbuf){ memcpy(buf+inbuf, ftpbuf, inftpbuf); inbuf += inftpbuf; memcpy(buf+inbuf, "
", 4); inbuf += 4; } ftps = ftpcommand(param, (unsigned char *)"LIST", NULL); mode = 1; } if(ftps == INVALID_SOCKET){RETURN(780);} if(!mode){ send_st(param, 8); s = param->remsock; param->remsock = ftps; if((param->operation == FTP_PUT) && (contentlength64 > 0)) param->waitclient64 = contentlength64; res = mapsocket(param, conf.timeouts[CONNECTION_L]); if (res == 99) res = 0; param->srv->so._closesocket(param->sostate, ftps); ftps = INVALID_SOCKET; param->remsock = s; } else { int headsent = 0; int gotres = -1; s = param->remsock; if(param->srvoffset < param->srvinbuf){ gotres = ftpres(param, buf+inbuf, bufsize-(inbuf+100)); if(gotres) inbuf= (int)strlen((char *)buf); } param->remsock = ftps; if(gotres <= 0) for(; (res = sockgetlinebuf(param, SERVER, (unsigned char *)ftpbuf, FTPBUFSIZE - 20, '\n', conf.timeouts[STRING_S])) > 0; i++){ int isdir = 0; int islink = 0; int filetoken =-1; int sizetoken =-1; int modetoken =-1; int datetoken =-1; unsigned char * tokens[10]; unsigned wordlen [10]; unsigned char j=0; int space = 1; ftpbuf[res] = 0; if(!i && ftpbuf[0] == 't' && ftpbuf[1] == 'o' && ftpbuf[2] == 't'){ mode = 2; continue; } if(!isnumber(*ftpbuf) && mode == 1) mode = 2; for(sb=(unsigned char *)ftpbuf; *sb; sb++){ if(!space && isspace(*sb)){ space = 1; wordlen[j]=(unsigned)(sb-tokens[j]); j++; } if(space && !isspace(*sb)){ space = 0; tokens[j] = sb; if(j==8)break; } } if(mode == 1){ if(j < 4) continue; if(!(isdir = !memcmp(tokens[2], "", wordlen[2])) && !isnumber(*tokens[2])){ continue; } datetoken = 0; wordlen[datetoken] = ((unsigned)(tokens[1] - tokens[0])) + wordlen[1]; sizetoken = 2; filetoken = 3; } else { if(j < 8 || wordlen[0]!=10) continue; if(j < 8 || !isnumber(*tokens[4])) mode = 3; if(*tokens[0] == 'd') isdir = 1; if(*tokens[0] == 'l') islink = 1; modetoken = 0; sizetoken = (mode == 2)? 4:3; filetoken = (mode == 2)? 8:7; datetoken = (mode == 2)? 5:4; tokens[filetoken] = tokens[filetoken-1]; while(*tokens[filetoken] && !isspace(*tokens[filetoken]))tokens[filetoken]++; if(*tokens[filetoken]){ tokens[filetoken]++; } wordlen[datetoken] = (unsigned)(tokens[filetoken] - tokens[datetoken]); wordlen[filetoken] = (unsigned)strlen((char *)tokens[filetoken]); } if(modetoken >= 0) memcpy(buf+inbuf, tokens[modetoken], 11); else memcpy(buf+inbuf, "---------- ", 11); inbuf += 11; if((int) wordlen[datetoken]+256 > bufsize-inbuf) continue; memcpy(buf+inbuf, tokens[datetoken], wordlen[datetoken]); inbuf += wordlen[datetoken]; if(isdir){ memcpy(buf+inbuf, " DIR", 10); inbuf+=10; } else if(islink){ memcpy(buf+inbuf, " LINK", 10); inbuf+=10; } else{ unsigned k; if(wordlen[sizetoken]>10) wordlen[sizetoken] = 10; for(k=10; k > wordlen[sizetoken]; k--){ buf[inbuf++] = ' '; } memcpy(buf+inbuf, tokens[sizetoken], wordlen[sizetoken]); inbuf+=wordlen[sizetoken]; } memcpy(buf+inbuf, " "); if(sb) sb+=4; else sb=tokens[filetoken]; if(*sb != '/' && ftpbase)file2url(ftpbase, buf, bufsize, (int *)&inbuf, 1); file2url(sb, buf, bufsize, (int *)&inbuf, 0); if(isdir)buf[inbuf++] = '/'; memcpy(buf+inbuf, "\">", 2); inbuf+=2; for(sb=tokens[filetoken]; *sb; sb++){ if((bufsize - inbuf)<16)break; if(*sb == '<'){ memcpy(buf+inbuf, "<", 4); inbuf+=4; } else if(*sb == '>'){ memcpy(buf+inbuf, ">", 4); inbuf+=4; } else if(*sb == '\r' || *sb=='\n'){ continue; } else if(islink && sb[0] == ' ' && sb[1] == '-' && sb[2] == '>'){ memcpy(buf+inbuf, " ", 5); inbuf+=5; } else buf[inbuf++]=*sb; } if(islink!=2){ memcpy(buf+inbuf, "", 4); inbuf+=4; } buf[inbuf++] = '\n'; if((bufsize - inbuf) < LINESIZE){ if (bufsize > 20000){ if(!headsent++){ send_st(param, 9); } if((unsigned)socksend(param, param->clisock, buf, inbuf, conf.timeouts[STRING_S]) != inbuf){ param->srv->so._closesocket(param->sostate, ftps); ftps = INVALID_SOCKET; param->remsock = s; RETURN(781); } inbuf = 0; } else { if(!(newbuf = realloc(buf, bufsize + BUFSIZE))){ param->srv->so._closesocket(param->sostate, ftps); ftps = INVALID_SOCKET; param->remsock = s; RETURN (21); } buf = newbuf; bufsize += BUFSIZE; } } } memcpy(buf+inbuf, "
", 4); inbuf += 4; param->srv->so._closesocket(param->sostate, ftps); ftps = INVALID_SOCKET; param->remsock = s; if(inbuf){ buf[inbuf] = 0; if(gotres < 0 ) res = ftpres(param, buf+inbuf, bufsize-inbuf); else res = gotres; inbuf = (int)strlen((char *)buf); if(!headsent){ sprintf(ftpbuf, "HTTP/1.0 200 OK\r\n" "Content-Type: text/html\r\n" "Connection: keep-alive\r\n" "Content-Length: %d\r\n\r\n", inbuf); socksend(param, param->clisock, (unsigned char *)ftpbuf, (int)strlen(ftpbuf), conf.timeouts[STRING_S]); } socksend(param, param->clisock, buf, inbuf, conf.timeouts[STRING_S]); if(res){RETURN(res);} if(!headsent)goto REQUESTEND; } RETURN(0); } RETURN(res); } if(isconnect && param->redirtype != R_HTTP) { if(param->redirectfunc) { freeptr(&req); freeptr(&buf); freeptr(&ftpbase); return (void *)param->redirectfunc; } param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); RETURN(param->res); } if(!req || param->redirtype != R_HTTP) { reqlen = 0; } else { #ifdef TCP_CORK int opt = 1; param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_TCP, TCP_CORK, (unsigned char *)&opt, sizeof(int)); #endif redirect = 1; res = (int)strlen((char *)req); if(socksend(param, param->remsock, req , res, conf.timeouts[STRING_L]) != res) { RETURN(518); } param->statscli64 += res; param->nwrites++; } inbuf = 0; { int hlen = (int)strlen((char *)buf); #ifndef ANONYMOUS if(!anonymous){ hlen += sprintf((char*)buf + hlen, "Forwarded: for="); if(*SAFAMILY(¶m->sincr) == AF_INET6) hlen += sprintf((char*)buf + hlen, "\"["); hlen += myinet_ntop(*SAFAMILY(¶m->sincr), SAADDR(¶m->sincr), (char *)buf + hlen, 128); if(*SAFAMILY(¶m->sincr) == AF_INET6) hlen += sprintf((char*)buf + hlen, "]:%d\";by=", (int)ntohs(*SAPORT(¶m->sincr))); else hlen += sprintf((char*)buf + hlen, ":%d;by=", (int)ntohs(*SAPORT(¶m->sincr))); gethostname((char *)(buf + hlen), 256); hlen += (int)strlen((char *)(buf + hlen)); hlen += sprintf((char*)buf + hlen, ":%d\r\n", (int)ntohs(*SAPORT(¶m->sincl))); } else if(anonymous>1){ hlen += sprintf((char*)buf + hlen, "Via: 1.1 "); gethostname((char *)(buf + hlen), 256); hlen += (int)strlen((char *)(buf + hlen)); hlen += sprintf((char*)buf + hlen, ":%d (%s %s)\r\nX-Forwarded-For: ", (int)ntohs(*SAPORT(¶m->srv->intsa)), conf.stringtable?conf.stringtable[2]:(unsigned char *)"", conf.stringtable?conf.stringtable[3]:(unsigned char *)""); if(anonymous != 2)hlen += myinet_ntop(*SAFAMILY(¶m->sincr), SAADDR(¶m->sincr), (char *)buf + hlen, 128); else { uint32_t tmp = myrand(); hlen += myinet_ntop(AF_INET, &tmp, (char *)buf + hlen, 64); } hlen += sprintf((char*)buf + hlen, "\r\n"); } #endif if(keepalive <= 1) { hlen += sprintf((char*)buf + hlen, "Connection: %s\r\n", keepalive? "keep-alive":"close"); } if(param->extusername){ hlen += sprintf((char*)buf + hlen, "%s: Basic ", (redirect)?"Proxy-Authorization":"Authorization"); sprintf((char*)username, "%.128s:%.128s", param->extusername, param->extpassword?param->extpassword:(unsigned char*)""); if(!(sb = en64(username, buf + hlen, (int)strlen((char *)username), bufsize - hlen))) {RETURN(21);} hlen = (int)(sb - buf); hlen += sprintf((char*)buf + hlen, "\r\n"); } hlen += sprintf((char*)buf + hlen, "\r\n"); if ((res = socksend(param, param->remsock, buf+reqlen, hlen - reqlen, conf.timeouts[STRING_S])) != hlen - reqlen) { RETURN(518); } } #ifdef TCP_CORK { int opt = 0; param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_TCP, TCP_CORK, (unsigned char *)&opt, sizeof(int)); } #endif param->statscli64 += res; param->nwrites++; if(param->bandlimfunc) { sleeptime = param->bandlimfunc(param, 0, (int)strlen((char *)buf)); } if(chunkedcli || contentlength64 > 0){ if(chunkedcli){ do { unsigned char smallbuf[32]; while ((i = sockgetlinebuf(param, CLIENT, smallbuf, 30, '\n', conf.timeouts[STRING_S])) == 2) { if (socksend(param, param->remsock, smallbuf, i, conf.timeouts[STRING_S]) != i) RETURN(536); if(chunkedcli == 2) break; } if(i<3) break; if (socksend(param, param->remsock, smallbuf, i, conf.timeouts[STRING_S]) != i) RETURN(536); if(chunkedcli == 2) { if((i = sockgetlinebuf(param, CLIENT, smallbuf, 30, '\n', conf.timeouts[STRING_S])) != 2) RETURN(537); if (socksend(param, param->remsock, smallbuf, i, conf.timeouts[STRING_S]) != i) RETURN(536); break; } smallbuf[i] = 0; contentlength64 = 0; if(sscanf((char *)smallbuf, "%"SCNx64"", &contentlength64) != 1) RETURN(537); /* chunk-size line may be longer than smallbuf: drain and relay the remainder up to LF so client/backend framing stays in sync */ while(i > 0 && smallbuf[i-1] != '\n'){ if((i = sockgetlinebuf(param, CLIENT, smallbuf, 30, '\n', conf.timeouts[STRING_S])) <= 0) RETURN(537); if (socksend(param, param->remsock, smallbuf, i, conf.timeouts[STRING_S]) != i) RETURN(536); } if(contentlength64 == 0) chunkedcli = 2; else { param->waitclient64 = contentlength64; res = mapsocket(param, conf.timeouts[CONNECTION_S]); param->waitclient64 = 0; if(res != 99) RETURN(res); } } while(chunkedcli); res = 99; } else { param->waitclient64 = contentlength64; res = mapsocket(param, conf.timeouts[CONNECTION_S]); param->waitclient64 = 0; if(res != 99) { RETURN(res); } } } contentlength64 = 0; inbuf = 0; ckeepalive = keepalive; res = 0; authenticate = 0; param->chunked = 0; hascontent = 0; clhdrofs = -1; while( (i = sockgetlinebuf(param, SERVER, buf + inbuf, LINESIZE - 1, '\n', conf.timeouts[(res)?STRING_S:STRING_L])) > 2) { if(!res && i>9)param->status = res = atoi((char *)buf + inbuf + 9); if(((i >= 25 && !strncasecmp((char *)(buf+inbuf), "proxy-connection:", 17)) || (i> 16 && !strncasecmp((char *)(buf+inbuf), "connection:", 11)) )){ sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; if(strncasecmp((char *)sb,"keep-alive", 10))ckeepalive = 0; if(!param->srv->transparent && res >= 200)continue; } else if(i> 6 && !param->srv->transparent && (!strncasecmp((char *)(buf+inbuf), "proxy-", 6))){ continue; } else if(i> 6 && (!strncasecmp((char *)(buf+inbuf), "www-authenticate", 16))){ authenticate = 1; } else if(i > 15 && (!strncasecmp((char *)(buf+inbuf), "content-length", 14))){ if(param->chunked) continue; buf[inbuf+i]=0; sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; sscanf((char *)sb, "%"SCNu64"", &contentlength64); hascontent = 1; clhdrofs = inbuf; if(param->unsafefilter && param->ndatfilterssrv > 0) { hascontent = 2; continue; } if(param->maxtrafin64 && (param->maxtrafin64 < param->statssrv64 || contentlength64 + param->statssrv64 > param->maxtrafin64)){ RETURN(10); } } else if(i>25 && (!strncasecmp((char *)(buf+inbuf), "transfer-encoding", 17))){ buf[inbuf+i]=0; sb = (unsigned char *)strchr((char *)(buf+inbuf), ':'); if(!sb)continue; ++sb; while(isspace(*sb))sb++; if(!strncasecmp((char *)sb, "chunked", 7)){ param->chunked = 1; if(clhdrofs >= 0){ buf[clhdrofs] = 'X'; clhdrofs = -1; contentlength64 = 0; hascontent = 0; } } } inbuf += i; if((bufsize - inbuf) < LINESIZE){ if (bufsize > 20000){ RETURN (516); } if(!(newbuf = realloc(buf, bufsize + BUFSIZE))){RETURN (21);} buf = newbuf; bufsize += BUFSIZE; } } if(res < 200 || res > 499) { ckeepalive = 0; } if((res == 304 || res == 204) && !hascontent){ hascontent = 1; contentlength64 = 0; } if(param->bandlimfunc) { int st1; st1 = (*param->bandlimfunc)(param, inbuf, 0); if(st1 > sleeptime) sleeptime = st1; if(sleeptime > 0){ /* if(sleeptime > 30) sleeptime = 30; */ usleep(sleeptime * SLEEPTIME); } } buf[inbuf] = 0; if(inbuf < 9) {RETURN (522);} #ifndef WITHMAIN action = handlehdrfilterssrv(param, &buf, &bufsize, 0, &inbuf); if(action == HANDLED){ RETURN(0); } if(action != PASS) RETURN(517); if(growbuf(&buf, &bufsize, inbuf + HDRRESERVE)) RETURN(21); param->nolongdatfilter = 0; if (conf.filtermaxsize && contentlength64 > (uint64_t)conf.filtermaxsize) { param->nolongdatfilter = 1; } else if(param->ndatfilterssrv > 0 && contentlength64 > 0 && contentlength64 == (uint64_t)(unsigned long)contentlength64 && param->operation != HTTP_HEAD && res != 204 && res != 304){ uint64_t newlen; newlen = (uint64_t)sockfillbuffsrv(param, (unsigned long) contentlength64, CONNECTION_S); if(newlen == contentlength64) { action = handlepredatflt(param); if(action == HANDLED){ RETURN(0); } if(action != PASS) RETURN(19); action = handledatfltsrv(param, ¶m->srvbuf, (int *)¶m->srvbufsize, 0, (int *)¶m->srvinbuf); param->nolongdatfilter = 1; if(action == HANDLED){ RETURN(0); } if(action != PASS) RETURN(517); contentlength64 = param->srvinbuf; if(growbuf(&buf, &bufsize, (int)strlen((char *)buf) + CLHDRSIZE)) RETURN(21); sprintf((char*)buf+strlen((char *)buf), "Content-Length: %"PRIu64"\r\n", contentlength64); hascontent = 1; } } if (contentlength64 > 0 && hascontent != 1) ckeepalive = 0; #else #endif if(!isconnect || param->operation){ int hlen = (int)strlen((char *)buf); if(authenticate && !param->transparent) hlen += sprintf((char*)buf + hlen, "Proxy-support: Session-Based-Authentication\r\n" "Connection: Proxy-support\r\n" ); if(!param->srv->transparent && res>=200){ if(ckeepalive <= 1) hlen += sprintf((char*)buf + hlen, "Connection: %s\r\n", (hascontent && ckeepalive)?"keep-alive":"close"); } hlen += sprintf((char*)buf + hlen, "\r\n"); if(socksend(param, param->clisock, buf, hlen, conf.timeouts[STRING_S]) != hlen) { RETURN(521); } } if((param->chunked || contentlength64 > 0) && param->operation != HTTP_HEAD && res != 204 && res != 304) { do { if(param->chunked){ unsigned char smallbuf[32]; while ((i = sockgetlinebuf(param, SERVER, smallbuf, 30, '\n', conf.timeouts[STRING_S])) == 2) { if (socksend(param, param->clisock, smallbuf, i, conf.timeouts[STRING_S]) != i){ RETURN(533); } if(param->chunked == 2) break; } if(i<3) { keepalive = 0; break; } if (socksend(param, param->clisock, smallbuf, i, conf.timeouts[STRING_S]) != i){ RETURN(535); } if(param->chunked == 2) { if((i = sockgetlinebuf(param, SERVER, smallbuf, 30, '\n', conf.timeouts[STRING_S])) != 2) RETURN(534); if (socksend(param, param->clisock, smallbuf, i, conf.timeouts[STRING_S]) != i){ RETURN(533); } break; } smallbuf[i] = 0; contentlength64 = 0; if(sscanf((char *)smallbuf, "%"SCNx64"", &contentlength64) != 1) { keepalive = 0; break; } /* chunk-size line may be longer than smallbuf: drain and relay the remainder up to LF so client/backend framing stays in sync */ while(i > 0 && smallbuf[i-1] != '\n'){ if((i = sockgetlinebuf(param, SERVER, smallbuf, 30, '\n', conf.timeouts[STRING_S])) <= 0) RETURN(534); if (socksend(param, param->clisock, smallbuf, i, conf.timeouts[STRING_S]) != i) RETURN(535); } if(contentlength64 == 0) { param->chunked = 2; } } if(param->chunked != 2){ param->waitserver64 = contentlength64; if((res = mapsocket(param, conf.timeouts[CONNECTION_S])) != 98){ RETURN(res); } param->waitserver64 = 0; } } while(param->chunked); } if(isconnect && res == 200 && param->operation){ RETURN (mapsocket(param, conf.timeouts[CONNECTION_S])); } else if(isconnect){ ckeepalive = keepalive = 1; } else if(!hascontent && !param->chunked) { RETURN(mapsocket(param, conf.timeouts[CONNECTION_S])); } contentlength64 = 0; REQUESTEND: if((!ckeepalive || !keepalive) && param->remsock != INVALID_SOCKET){ param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR); param->srv->so._closesocket(param->sostate, param->remsock); param->remsock = INVALID_SOCKET; RETURN(0); } if(param->transparent && (!ckeepalive || !keepalive)) {RETURN (0);} /* Another service read this request and handed it here to be answered. It keeps the connection and decides what the next request on it is, so this one is done. Whatever was opened towards the server stays open in param for the next one. */ if(param->onerequest){ /* 2 says the client connection may carry another request, 1 that it may not, which is what the service holding it needs to know. */ param->onerequest = (ckeepalive && keepalive)? 2 : 1; RETURN(0); } logurl(param, (char *)buf, (char *)req, ftp); param->status = 0; } CLEANRET: if(param->res != 555 && param->res && param->clisock != INVALID_SOCKET && (param->res < 90 || param->res >=800 || param->res == 100 ||(param->res > 500 && param->res< 800))) { int stidx = -1; int r = param->res; if((r >= 509 && r < 517) || r > 900) { if(buf) while( (i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 1, '\n', conf.timeouts[STRING_S])) > 2); } if(r == 10) stidx = 2; else if(r == 700 || r == 701) stidx = 16; else if(r == 100 || (r > 10 && r < 20) || (r > 701 && r <= 705)) stidx = 1; else if(r >= 20 && r < 30) stidx = 6; else if(r >= 30 && r < 80) stidx = 5; else if(r == 1 || (!param->srv->needuser && r < 10)) stidx = 11; else if(r < 10) stidx = 7; else if(r == 999) stidx = 4; else if(r == 519) stidx = 3; else if(r == 517) stidx = 15; else if(r == 780) stidx = 10; else if(r >= 511 && r <= 516) stidx = 0; if(stidx >= 0) { send_st(param, stidx); if(r == 700 || r == 701) socksend(param, param->clisock, (unsigned char *)ftpbuf, inftpbuf, conf.timeouts[STRING_S]); } } logurl(param, (char *)buf, (char *)req, ftp); freeptr(&req); freeptr(&buf); freeptr(&ftpbase); return (NULL); } #ifdef WITHMAIN struct proxydef childdef = { proxychild, 3128, 0, S_PROXY, "-a - anonymous proxy\r\n" "-a1 - anonymous proxy with random client IP spoofing\r\n" }; #include "proxymain.c" #endif