From 6b579b8190b25a3c2edbc521ba38c73707b01c10 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 1 Jul 2014 19:08:31 +0200 Subject: [PATCH] Communicate SOCKS error states properly This used to look pretty random :( Try to keep the last decimal digits of internal errors sensible for v5 error codes. For most cases we can't really do much better than using the value of one. One specific code paths where we can return something more useful is the authentication, where we not communicate the underlying error code or signal an ACL denial. As for v4, there's not really much space to be too smart as only the code of 91 is meaningful. Therefore we limit the last digit to zero and one now. --- src/socks.c | 66 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/src/socks.c b/src/socks.c index 6cdff57..a48ffbf 100644 --- a/src/socks.c +++ b/src/socks.c @@ -48,29 +48,29 @@ void * sockschild(struct clientparam* param) { if(ver == 5){ if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} /* nmethods */ for (; i; i--) { - if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(442);} + if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} if (res == 2 && !param->srv->nouser) { havepass = res; } } buf[0] = 5; buf[1] = havepass; - if(socksend(param->clisock, buf, 2, conf.timeouts[STRING_S])!=2){RETURN(402);} + if(socksend(param->clisock, buf, 2, conf.timeouts[STRING_S])!=2){RETURN(401);} if (havepass) { if (((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_L], 0))) != 1) { RETURN(412); } - if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(443);} - if (i && (unsigned)(res = sockgetlinebuf(param, CLIENT, buf, i, 0, conf.timeouts[STRING_S])) != i){RETURN(444);}; + if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(451);} + if (i && (unsigned)(res = sockgetlinebuf(param, CLIENT, buf, i, 0, conf.timeouts[STRING_S])) != i){RETURN(441);}; buf[i] = 0; if(!param->username)param->username = (unsigned char *)mystrdup((char *)buf); if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(445);} - if (i && (unsigned)(res = sockgetlinebuf(param, CLIENT, buf, i, 0, conf.timeouts[STRING_S])) != i){RETURN(446);}; + if (i && (unsigned)(res = sockgetlinebuf(param, CLIENT, buf, i, 0, conf.timeouts[STRING_S])) != i){RETURN(441);}; buf[i] = 0; if(!param->password)param->password = (unsigned char *)mystrdup((char *)buf); buf[0] = 1; buf[1] = 0; - if(socksend(param->clisock, buf, 2, conf.timeouts[STRING_S])!=2){RETURN(402);} + if(socksend(param->clisock, buf, 2, conf.timeouts[STRING_S])!=2){RETURN(481);} } if ((c = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_L], 0)) != 5) { RETURN(421); @@ -82,9 +82,9 @@ void * sockschild(struct clientparam* param) { c = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0); /* atype */ } else { - if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(448);} + if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[0] = (unsigned char) res; - if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(449);} + if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[1] = (unsigned char) res; param->sins.sin_port = param->req.sin_port = *(unsigned short*)buf; c = 1; @@ -93,19 +93,19 @@ void * sockschild(struct clientparam* param) { switch(c) { case 1: for (i = 0; i<4; i++){ - if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(450);} + if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[i] = (unsigned char)res; } param->sins.sin_addr.s_addr = param->req.sin_addr.s_addr = *(unsigned long *)buf; if(command==1 && !param->req.sin_addr.s_addr) { - RETURN(422); + RETURN(421); } myinet_ntop(*SAFAMILY(¶m->sins), SAADDR(¶m->sins), (char *)buf + strlen((char *)buf), 64); break; case 3: if ((size = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(451);} /* nmethods */ for (i=0; ihostname)myfree(param->hostname); param->hostname = (unsigned char *)mystrdup((char *)buf); if (ver == 5) { - if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(453);} + if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[0] = (unsigned char) res; - if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(454);} + if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[1] = (unsigned char) res; param->sins.sin_port = param->req.sin_port = *(unsigned short*)buf; } @@ -139,7 +139,7 @@ void * sockschild(struct clientparam* param) { param->sins.sin_addr.s_addr = param->req.sin_addr.s_addr = getip(buf); } } - if(command == 1 && !param->req.sin_port) {RETURN(424);} + if(command == 1 && !param->req.sin_port) {RETURN(421);} param->sins.sin_family = AF_INET; switch(command) { case 1: @@ -161,12 +161,38 @@ void * sockschild(struct clientparam* param) { RETURN(997); } - if((res = (*param->srv->authfunc)(param))) {RETURN(res);} + errno = 0; + if((res = (*param->srv->authfunc)(param))) { + res *= 10; + switch (errno) { + /* If authfunc failed but errno stays intacts we assume ACL denied the access, + * otherwise we do our best to pick a good error code for SOCKSv5. */ + case 0: + res += 2; /* connection not allowed by ruleset */ + break; + case ENETUNREACH: + res += 3; /* Network unreachable */ + break; + case EHOSTUNREACH: + res += 4; /* Host unreachable */ + break; + case ECONNREFUSED: + res += 5; /* Connection refused */ + break; + case EPFNOSUPPORT: + case EAFNOSUPPORT: + res += 8; /* Address type not supported */ + break; + default: + res += 1; + } + RETURN(res); + } if(command > 1) { if(so._bind(param->remsock,(struct sockaddr *)¶m->sins,sizeof(param->sins))) { param->sins.sin_port = 0; - if(so._bind(param->remsock,(struct sockaddr *)¶m->sins,sizeof(param->sins)))RETURN (12); + if(so._bind(param->remsock,(struct sockaddr *)¶m->sins,sizeof(param->sins)))RETURN (11); #if SOCKSTRACE > 0 fprintf(stderr, "%s:%hu binded to communicate with server\n", inet_ntoa(param->sins.sin_addr), @@ -183,7 +209,7 @@ fflush(stderr); if(param->clisock == INVALID_SOCKET) {RETURN(11);} memcpy(&sin, ¶m->sincl, sizeof(&sin)); *SAPORT(&sin) = htons(0); - if(so._bind(param->clisock,(struct sockaddr *)&sin,sizeof(sin))) {RETURN (12);} + if(so._bind(param->clisock,(struct sockaddr *)&sin,sizeof(sin))) {RETURN (11);} #if SOCKSTRACE > 0 fprintf(stderr, "%hu binded to communicate with client\n", ntohs(*SAPORT(&sin)) @@ -220,7 +246,7 @@ fflush(stderr); } else{ buf[0] = 0; - buf[1] = 90 + (param->res%10); + buf[1] = 90 + !!(param->res%10); memcpy(buf+2, SAPORT(&sin), 2); memcpy(buf+4, SAADDR(&sin), 4); socksend(param->clisock, buf, 8, conf.timeouts[STRING_S]); @@ -329,7 +355,7 @@ fflush(stderr); param->sins.sin_addr.s_addr = getip(buf+4); break; default: - RETURN(996); + RETURN(997); } memcpy(¶m->sins.sin_port, buf+i, 2);