#include #include #include #include #include #include #include #include #include #include #include #include #include #define ME "connect: " #define strncpyz(d,s,l) *(strncpy(d,s,l)+l)=0 #define lwrcase(a) ((a>64 && a<91) ? a+32:a) #define uprcase(a) ((a>96 && a<123) ? a-32:a) #define BadPtr(ptr) (!ptr || !*ptr) #define H (2*sizeof(void *)) #define Tail(ptr) (*((void **)ptr-2)) // only on head #define Prev(ptr) (*((void **)ptr-2)) #define Next(ptr) (*((void **)ptr-1)) #define PrevA(ptr) ptr=*((void **)ptr-2) #define NextA(ptr) ptr=*((void **)ptr-1) #define DBUFSZ 4080 #define DBUF_POOL 2048 #define PS 8188 #define QS 65536 struct dbufbuf_s { struct dbufbuf_s *link; char buf[DBUFSZ]; }; struct dbuf_s { struct dbufbuf_s *head; struct dbufbuf_s *tail; int len; int off; }; typedef struct dbuf_s dbuf_t; #define dbuf_len(a) ((a)->len) static struct dbufbuf_s *freelist=0; static int dbuf_blocks=0; static struct sockaddr_in socks_addr; static char *inclist=0; static char *exclist=0; static char verb=0; static char ctty=0; static struct dbufbuf_s *dbuf_alloc(void) { struct dbufbuf_s *db; if(freelist) { db=freelist; freelist=freelist->link; db->link=0; return db; } if(dbuf_blocks*DBUFSZ>DBUF_POOL*1024) return 0; if(!(db=malloc(sizeof(struct dbufbuf_s)))) return 0; db->link=0; dbuf_blocks++; return db; } static int dbuf_put(dbuf_t *dbuf,char *pt,int sz) { struct dbufbuf_s *db=0; int off,chunk; if(!sz) return 0; off=(dbuf->off+dbuf->len)%DBUFSZ; chunk=DBUFSZ-off; if(!dbuf->len) { if(!(db=dbuf_alloc())) return 0; dbuf->head=db; dbuf->tail=db; } else { if(off) db=dbuf->tail; } dbuf->len+=sz; while(sz) { if(!db) { if(!(db=dbuf_alloc())) return 0; dbuf->tail->link=db; dbuf->tail=db; } if(chunk>sz) chunk=sz; memcpy(db->buf+off,pt,chunk); sz-=chunk; pt+=chunk; db=0; off=0; chunk=DBUFSZ; } return 1; } static int dbuf_get(dbuf_t *dbuf,char *pt,int sz) { struct dbufbuf_s *db=dbuf->head; int off,chunk,len,size; if(!dbuf->len) return 0; size=0; len=dbuf->len; off=dbuf->off; chunk=DBUFSZ-off; while(sz && len) { if(chunk>len) chunk=len; if(chunk>sz) chunk=sz; memcpy(pt,db->buf+off,chunk); len-=chunk; sz-=chunk; pt+=chunk; size+=chunk; off=0; db=db->link; chunk=DBUFSZ; } return size; } static void dbuf_del(dbuf_t *dbuf,int sz) { struct dbufbuf_s *db=dbuf->head; int off,chunk; if(!dbuf->len) return; off=dbuf->off; chunk=DBUFSZ-off; while(sz && dbuf->len) { if(chunk>dbuf->len) chunk=dbuf->len; if(chunk>sz) chunk=sz; dbuf->len-=chunk; dbuf->off+=chunk; sz-=chunk; if(dbuf->off==DBUFSZ || !dbuf->len) { dbuf->head=db->link; db->link=freelist; freelist=db; db=dbuf->head; if(!dbuf->len) dbuf->tail=0; dbuf->off=0; } chunk=DBUFSZ; } } static void dbuf_clear(dbuf_t *dbuf) { struct dbufbuf_s *db=dbuf->head; if(!dbuf->len) return; dbuf->len=0; dbuf->off=0; dbuf->tail=0; while(db) { dbuf->head=db->link; db->link=freelist; freelist=db; db=dbuf->head; } } static inline void *_Append(void **head,size_t size) { void *temp; if(!(temp=malloc(size+H))) return 0; temp=(char *)temp+H; memset(temp,0,size); Next(temp)=0; if(*head) { Prev(temp)=Tail(*head); Next(Tail(*head))=temp; Tail(*head)=temp; return temp; } Tail(temp)=temp; *head=temp; return temp; } static inline void *_Delete(void **head,void *ptr) { void *ret; if(ptr==*head) { if(Next(ptr)) { Tail(Next(ptr))=Tail(ptr); ret=Next(ptr); *head=ret; free((char *)ptr-H); return ret; } free((char *)ptr-H); *head=0; return 0; } if(ptr==Tail(*head)) Tail(*head)=Prev(ptr); if(Prev(ptr)) Next(Prev(ptr))=Next(ptr); if(Next(ptr)) Prev(Next(ptr))=Prev(ptr); ret=Next(ptr); free((char *)ptr-H); return ret; } static inline void _Clear(void **head) { void *ptr,*tmp; ptr=*head; while(ptr) { tmp=ptr; ptr=Next(ptr); free((char *)tmp-H); } *head=0; } #define Append(base,size) _Append((void **)&base,size) #define Delete(base,ptr) _Delete((void **)&base,ptr) #define Clear(base) _Clear((void **)&base) static char isstrdec(char *s,size_t l) { while(l--) { if(*s<48 || *s>57) return 0; } return 1; } static char isstrhex(char *s,size_t l) { while(l--) { if(*s<48 || (*s>57 && *s<65) || (*s>70 && *s<97) || *s>102) return 0; } return 1; } static char bad_inaddr(void *in) { int i; for(i=0;i<4;i++) { if(*((u_char *)in+i)!=255) return 0; } return 1; } static char any_inaddr(void *in) { int i; for(i=0;i<4;i++) { if(*((u_char *)in+i)!=0) return 0; } return 1; } static char lo_inaddr(void *in) { if(*(u_char *)in==127) return 1; return 0; } static char *inetntoa(void *in) { static char buf[8][16]; static int z=0; u_char *s=(u_char *)in; int a,b,c,d; z=(z+1)&7; a=(int)*s++; b=(int)*s++; c=(int)*s++; d=(int)*s++; sprintf(buf[z],"%d.%d.%d.%d",a,b,c,d); return buf[z]; } static char inetaton(char *s,void *d) { char *p; int i=0; memset(d,-1,4); p=s; while(*p && i<4) { if(!(p=strchr(s,'.'))) { if(*s=='*') { for(;i<4;i++) *((u_char *)d+i)=0; return 1; } p=strchr(s,0); } if((p-s)>3 || !isstrdec(s,p-s)) { memset(d,-1,4); return 0; } *((u_char *)d+i)=(u_char)atoi(s); i++; s=p+1; } if(*p || i!=4) { memset(d,-1,4); return 0; } return 1; } static char *cmpstr(char *a,char *b) { char *c,*d,*t; t=b; while(1) { c=a; d=t; while(*c && lwrcase(*c)==lwrcase(*d)) { c++; d++; } if(!*c) return(d); t++; if(!*t) return(0); } } static char iswm(char *s,char *w) { char st[128]; char *a,*p; if(!s || !w || !*s || !*w) return 0; a=s; while(1) { while(*w=='?') { w++; a++; if(!*a) { if(!*w) return 1; return 0; } } while(*w=='*') { w++; if(!*w) return 1; p=st; while(*w && *w!='*' && *w!='?') *p++=*w++; *p=0; while(1) { a=cmpstr(st,a); if(!a) return 0; if(*w) break; if(!*a) return 1; } } while(*w && *w!='*' && *w!='?' && lwrcase(*a)==lwrcase(*w)) { a++; w++; } if(!*w) { if(!*a) return 1; return 0; } if(*w!='*' && *w!='?') return 0; } return 0; } static char ip_match(char *s,char *m) { char mask[64],str_s[24],str_m[24]; char *p,*p1; u_long bf; u_long ip_s,ip_m; int t; if(strlen(m)>40) return 0; strcpy(mask,m); if(!(p=strchr(mask,'/'))) return iswm(s,mask); *p=0; p++; bf=atoi(p); if(bf>32) return 0; if(strchr(mask,'*')) return iswm(s,mask); inetaton(mask,&ip_m); inetaton(s,&ip_s); if(ip_m==0xffffffff) return 0; bf=htonl(0xffffffff<<(32-bf)); ip_m&=bf; if((ip_s&bf)==ip_m) return 1; return 0; } static int mk_relay(char *host,int port) { static char buf[128]; char haddr[4],hport[2]; char *user; char *p; int s,l,r; *(unsigned short *)hport=htons(port); if(!inetaton(host,haddr)) { struct hostent *he; if(!(he=gethostbyname(host))) { fprintf(stderr,ME "Unable to resolve %s\n",host); exit(1); } if(he->h_length!=4) { fprintf(stderr,ME "Error resolving %s\n",host); exit(1); } memcpy(haddr,he->h_addr,he->h_length); } if(!(user=getenv("USER"))) { fprintf(stderr,ME "Unable to determine USER\n"); exit(1); } if((s=socket(AF_INET,SOCK_STREAM,0))<0) { fprintf(stderr,ME "socket() failed\n"); exit(1); } if(connect(s,(struct sockaddr *)&socks_addr,sizeof(struct sockaddr_in))<0) { close(s); fprintf(stderr,ME "connect() failed\n"); exit(1); } p=buf; // SOCKS4 proto // send: VN:1 CD:1 PORT:2 ADDR:4 USER:n 0:1 // recv: VN:1 CD:1 PORT:2 ADDR:4 *p++=4; *p++=1; *p++=hport[0]; *p++=hport[1]; *p++=haddr[0]; *p++=haddr[1]; *p++=haddr[2]; *p++=haddr[3]; l=strlen(user); strcpy(p,user); p+=l+1; l=p-buf; r=send(s,buf,l,0); if(rh_length!=4) { fprintf(stderr,ME "Error resolving %s\n",host); exit(1); } memcpy(&addr.sin_addr,he->h_addr,he->h_length); } addr.sin_family=AF_INET; addr.sin_port=htons(port); if((s=socket(AF_INET,SOCK_STREAM,0))<0) { fprintf(stderr,ME "socket() failed\n"); exit(1); } if(connect(s,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) { close(s); fprintf(stderr,ME "connect() failed\n"); exit(1); } return s; } static int mk_listen(int port) { struct sockaddr_in addr; char *ip,*p; int opt=1; int s,s2; int l; memset(&addr.sin_addr,0,4); addr.sin_family=AF_INET; addr.sin_port=htons(port); if((s=socket(AF_INET,SOCK_STREAM,0))<0) { fprintf(stderr,ME "socket() failed\n"); exit(1); } if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(int))<0) { close(s); fprintf(stderr,ME "setsockopt(SO_REUSEADDR) failed\n"); exit(1); } if(bind(s,(struct sockaddr *)&addr,sizeof(addr))<0) { close(s); fprintf(stderr,ME "bind() failed\n"); exit(1); } if(listen(s,1)<0) { close(s); fprintf(stderr,ME "listen() failed\n"); exit(1); } while(1) { l=sizeof(struct sockaddr_in); if((s2=accept(s,(struct sockaddr *)&addr,&l))<0) { close(s); fprintf(stderr,ME "accept() failed\n"); exit(1); } ip=inetntoa(&addr.sin_addr); if(inclist) { for(p=inclist;p;NextA(p)) { if(ip_match(ip,p)) break; } if(!p) { close(s2); continue; } } for(p=exclist;p;NextA(p)) { if(ip_match(ip,p)) break; } if(!p) break; close(s2); } close(s); return s2; } static int un_conn(char *path) { struct sockaddr_un addr; int s; addr.sun_family=AF_UNIX; strcpy(addr.sun_path,path); if((s=socket(AF_UNIX,SOCK_STREAM,0))<0) { fprintf(stderr,ME "socket() failed\n"); exit(1); } if(connect(s,(struct sockaddr *)&addr,sizeof(addr))<0) { close(s); fprintf(stderr,ME "connect() failed\n"); exit(1); } return s; } static int un_listen(char *path) { struct sockaddr_un addr; int s,s2; addr.sun_family=AF_UNIX; strcpy(addr.sun_path,path); if((s=socket(AF_UNIX,SOCK_STREAM,0))<0) { fprintf(stderr,ME "socket() failed\n"); exit(1); } if(bind(s,(struct sockaddr *)&addr,sizeof(addr))<0) { close(s); fprintf(stderr,ME "bind() failed\n"); exit(1); } if(listen(s,1)<0) { close(s); fprintf(stderr,ME "listen() failed\n"); exit(1); } if((s2=accept(s,0,0))<0) { close(s); unlink(path); fprintf(stderr,ME "accept() failed\n"); exit(1); } close(s); unlink(path); return s2; } static void do_relay(int s) { static char buf[8192]; static fd_set rds,wrs; struct timeval tw; struct timeval tc,to; dbuf_t qr,ql; unsigned long long tbytes; unsigned long long tb; unsigned long tms,ms,bytes; int r,l; char sch[]={'K','M','G','T'}; char cd[4]={0}; char fin=0; char k; memset(&qr,0,sizeof(dbuf_t)); memset(&ql,0,sizeof(dbuf_t)); tms=0,tbytes=0; ms=0,bytes=0; gettimeofday(&to,0); if(ctty) { int d; if(isatty(0) && (d=open("/dev/null",O_RDONLY,0))!=-1) { dup2(d,0); close(d); cd[0]=1; } if(isatty(1) && (d=open("/dev/null",O_WRONLY,0))!=-1) { dup2(d,1); close(d); cd[1]=1; } if((d=open("/dev/null",O_WRONLY,0))!=-1) { dup2(d,2); close(d); } verb=0; } if(verb) fprintf(stderr,"Transferred: 0 KB (0 kB/s) \n"); while(1) { if(fin==1 && !dbuf_len(&qr)) break; if(fin==2 && !dbuf_len(&ql)) break; FD_ZERO(&rds); FD_ZERO(&wrs); if(!fin && !cd[0] && dbuf_len(&qr)=500000) { ms=ms/1000; tbytes+=bytes; tms+=ms; tb=tbytes/1024; k=0; while(tb>94999 && k<3) { tb=tb/1024; k++; } if(verb) fprintf(stderr,"\033[1ATransferred: %llu %cB (%lu kB/s) \n",tb,sch[k],bytes/ms); ms=0,bytes=0; } tw.tv_sec=0; tw.tv_usec=500000; r=select(FD_SETSIZE,&rds,&wrs,0,&tw); if(r<0) { close(s); fprintf(stderr,ME "select() failed\n"); exit(1); } gettimeofday(&tc,0); ms+=((tc.tv_sec-to.tv_sec)*1000000)+(tc.tv_usec-to.tv_usec); to=tc; if(!r) continue; if(FD_ISSET(0,&rds)) { if((r=read(0,buf,PS))<0) { close(s); fprintf(stderr,ME "read() failed\n"); exit(1); } if(r>0) dbuf_put(&qr,buf,r); else fin=1; } if(FD_ISSET(s,&rds)) { if((r=recv(s,buf,PS,0))<0) { close(s); fprintf(stderr,ME "recv() failed\n"); exit(1); } if(r>0) { dbuf_put(&ql,buf,r); bytes+=r; } else { close(s); fin=2; } } if(FD_ISSET(1,&wrs)) { l=dbuf_get(&ql,buf,PS); r=write(1,buf,l); if(r<0 || !r) { close(s); fprintf(stderr,ME "write() failed\n"); exit(1); } dbuf_del(&ql,r); } if(FD_ISSET(s,&wrs)) { l=dbuf_get(&qr,buf,PS); r=send(s,buf,l,0); if(r<0 || !r) { close(s); fprintf(stderr,ME "send() failed\n"); exit(1); } dbuf_del(&qr,r); bytes+=r; } } if(fin!=2) close(s); ms=ms/1000; tbytes+=bytes; tms+=ms; if(tms<10) tms=10; tb=tbytes/1024; k=0; while(tb>94999 && k<3) { tb=tb/1024; k++; } if(verb) fprintf(stderr,"\033[1ATransferred: %llu %cB (%lu kB/s) \n",tb,sch[k],tbytes/tms); } static void usage(void) { printf("Usage: connect [-v] [-S ] \n"); printf(" connect [-v] [-I ] [-X ] -l \n"); printf(" connect -s [-l]\n"); exit(0); } int main(int argc,char *argv[]) { static char socks_host[80]; static char host[64]; static char path[96]; int port=0; int i; char opt_l=0; char opt_s=0; *socks_host=0; *host=0; for(i=1;ih_length!=4) { fprintf(stderr,ME "Error resolving %s\n",socks_host); exit(1); } memcpy(&socks_addr.sin_addr,he->h_addr,he->h_length); } socks_addr.sin_family=AF_INET; socks_addr.sin_port=htons(port); continue; } if(!strcmp(argv[i],"-I")) { char *str; i++; if(i>=argc || BadPtr(argv[i])) { printf(ME "option '-I' needs argument\n"); exit(1); } str=Append(inclist,strlen(argv[i])+1); strcpy(str,argv[i]); continue; } if(!strcmp(argv[i],"-X")) { char *str; i++; if(i>=argc || BadPtr(argv[i])) { printf(ME "option '-X' needs argument\n"); exit(1); } str=Append(exclist,strlen(argv[i])+1); strcpy(str,argv[i]); continue; } if(!strcmp(argv[i],"-l")) { opt_l=1; if(opt_s) continue; i++; if(argc95) { fprintf(stderr,ME "Invalid argument specified\n"); exit(1); } strcpy(path,argv[i]); continue; } fprintf(stderr,ME "Unknown option\n"); exit(1); } if(opt_s && *socks_host) { fprintf(stderr,ME "Cannot mix '-s' and '-S' options\n"); exit(1); } if(opt_l && *socks_host) { fprintf(stderr,ME "Cannot mix '-l' and '-S' options\n"); exit(1); } if(opt_s) { if(opt_l) do_relay(un_listen(path)); else do_relay(un_conn(path)); return 0; } if(opt_l) { do_relay(mk_listen(port)); return 0; } if(argc<=i+1) usage(); if(BadPtr(argv[i]) || BadPtr(argv[i+1])) { fprintf(stderr,ME "Invalid arguments\n"); exit(1); } strncpyz(host,argv[i],63); if(!(port=atoi(argv[i+1]))) { fprintf(stderr,ME "Invalid port specified\n"); exit(1); } if(*socks_host) do_relay(mk_relay(host,port)); else do_relay(mk_conn(host,port)); return 0; }