/* net.c -- handles: all raw network i/o This is hereby released into the public domain. Robey Pointer, robey@netcom.com */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #if HAVE_SYS_SELECT_H #include #endif #include #ifndef LINUX /* linux gets wacko if you include both */ #include #endif /* but virtually every other OS NEEDS both */ #include /* is this really necessary? */ #include #include #if HAVE_UNISTD_H #include #endif #include #include "eggdrop.h" #include "proto.h" #if !HAVE_GETDTABLESIZE #ifdef FD_SETSIZE #define getdtablesize() FD_SETSIZE #else #define getdtablesize() MAXDCC+10 #endif #endif /* context support */ extern int cx_line; extern char cx_file[]; extern char SBUF[]; extern int backgrd; extern int use_stderr; extern char botuser[]; /* hostname can be specified in the config file */ char hostname[121]=""; /* IP can be specified in the config file */ char myip[121]=""; /* socks server for firewall */ char firewall[121]=""; /* socks server port */ int firewallport=178; /* this is used by the net module to keep track of sockets and what's queued on them */ typedef struct { int sock; char flags; char *inbuf; char *outbuf; } sock_list; #define MAXSOCKS MAXDCC+10 sock_list socklist[MAXSOCKS]; /* enough to be safe */ /* types of proxy */ #define PROXY_SOCKS 1 #define PROXY_SUN 2 /* i need an UNSIGNED long for dcc type stuff */ unsigned long my_atoul(s) char *s; { unsigned long ret=0L; while ((*s>='0') && (*s<='9')) { ret*=10L; ret+=((*s)-'0'); s++; } return ret; } /* i read somewhere that memcpy() is broken on some machines */ /* it's easy to replace, so i'm not gonna take any chances, because it's */ /* pretty important that it work correctly here */ void my_memcpy(dest,src,len) char *dest,*src; int len; { while (len--) *dest++=*src++; } /* bzero() is bsd-only, so here's one for non-bsd systems */ void my_bzero(dest,len) char *dest; int len; { while (len--) *dest++=0; } /* initialize the socklist */ void init_net() { int i; for (i=0; ih_name); if (strchr(s,'.')!=NULL) return; if (hp->h_aliases[0] == NULL) fatal("Can't determine your hostname!",0); strcpy(s,hp->h_aliases[0]); if (strchr(s,'.')==NULL) fatal("Can't determine your hostname!",0); } /* get my ip number */ unsigned long getmyip() { struct hostent *hp; char s[121]; unsigned long ip; struct in_addr *in; /* could be pre-defined */ if (myip[0]) { if ((myip[strlen(myip)-1] >= '0') && (myip[strlen(myip)-1] <= '9')) return (unsigned long)inet_addr(myip); } gethostname(s,120); hp=gethostbyname(s); if (hp==NULL) fatal("Hostname self-lookup failed.",0); in=(struct in_addr *)(hp->h_addr_list[0]); ip=(unsigned long)(in->s_addr); ip=htonl(ip); return (unsigned long)ip; } void neterror(s) char *s; { switch(errno) { case EADDRINUSE: strcpy(s,"Address already in use"); break; case EADDRNOTAVAIL: strcpy(s,"Address invalid on remote machine"); break; case EAFNOSUPPORT: strcpy(s,"Address family not supported"); break; case EALREADY: strcpy(s,"Socket already in use"); break; case EBADF: strcpy(s,"Socket descriptor is bad"); break; case ECONNREFUSED: strcpy(s,"Connection refused"); break; case EFAULT: strcpy(s,"Namespace segment violation"); break; case EINPROGRESS: strcpy(s,"Operation in progress"); break; case EINTR: strcpy(s,"Timeout"); break; case EINVAL: strcpy(s,"Invalid namespace"); break; case EISCONN: strcpy(s,"Socket already connected"); break; case ENETUNREACH: strcpy(s,"Network unreachable"); break; case ENOTSOCK: strcpy(s,"File descriptor, not a socket"); break; case ETIMEDOUT: strcpy(s,"Connection timed out"); break; case ENOTCONN: strcpy(s,"Socket is not connected"); break; case EHOSTUNREACH: strcpy(s,"Host is unreachable"); break; case EPIPE: strcpy(s,"Broken pipe"); break; #ifdef ECONNRESET case ECONNRESET: strcpy(s,"Connection reset by peer"); break; #endif #ifdef EACCES case EACCES: strcpy(s,"Permission denied"); break; #endif case 0: strcpy(s,"Error 0"); break; default: sprintf(s,"Unforseen error %d",errno); break; } } /* request a normal socket for i/o */ void setsock(sock,options) int sock,options; { int i; int parm; for (i=0; i= '0') && (host[strlen(host)-1] <= '9')) { unsigned long ip; ip=inet_addr(host); x[0]=(ip>>24); x[1]=(ip>>16)&0xff; x[2]=(ip>>8)&0xff; x[3]=ip&0xff; } else { /* no, must be host.domain */ alarm(10); hp=gethostbyname(host); alarm(0); if (hp==NULL) { killsock(sock); return -2; } my_memcpy(x,(char *)hp->h_addr,hp->h_length); } sprintf(s,"\004\001%c%c%c%c%c%c%s",(port>>8)%256,(port%256),x[0],x[1],x[2], x[3],botuser); write(sock,s,strlen(s)+1); } else if (proxy==PROXY_SUN) { sprintf(s,"%s %d\n",host,port); write(sock,s,strlen(s)); } return sock; } /* starts a connection attempt to a socket */ /* returns <0 if connection refused: */ /* -1 neterror() type error */ /* -2 can't resolve hostname */ int open_telnet_raw(sock,server,sport) int sock; char *server; int sport; { struct sockaddr_in name; struct hostent *hp; int i,port,proxy=0; char host[121]; /* firewall? use socks */ if (firewall[0]) { if (firewall[0]=='!') { proxy=PROXY_SUN; strcpy(host,&firewall[1]); } else { proxy=PROXY_SOCKS; strcpy(host,firewall); } port=firewallport; } else { strcpy(host,server); port=sport; } /* this part does not work, but was suggested by tris for multi-hosted */ /* machines -- maybe tris can get it to work? :) */ #if 0 my_bzero((char *)&name,sizeof(struct sockaddr_in)); name.sin_family=AF_INET; name.sin_addr.s_addr=getmyip(); if (bind(sock,(struct sockaddr *)&name,sizeof(name))<0) { killsock(sock); return -1; } #endif my_bzero((char *)&name,sizeof(struct sockaddr_in)); name.sin_family=AF_INET; name.sin_port=htons(port); /* numeric IP? */ if ((host[strlen(host)-1] >= '0') && (host[strlen(host)-1] <= '9')) name.sin_addr.s_addr=inet_addr(host); else { /* no, must be host.domain */ alarm(10); hp=gethostbyname(host); alarm(0); if (hp==NULL) { killsock(sock); return -2; } my_memcpy((char *)&name.sin_addr,hp->h_addr,hp->h_length); name.sin_family=hp->h_addrtype; } for (i=0; ih_name); return s; } /* short routine to answer a connect received on a socket made previously */ /* by open_listen ... returns hostname of the caller & the new socket */ /* does NOT dispose of old "public" socket! */ int answer(sock,caller,ip,binary) int sock; char *caller; unsigned long *ip; int binary; { int new_sock,addrlen; struct sockaddr_in from; addrlen=sizeof(struct sockaddr); new_sock=accept(sock,(struct sockaddr *)&from,&addrlen); if (new_sock<0) return -1; *ip=from.sin_addr.s_addr; strcpy(caller,hostnamefromip(*ip)); *ip=ntohl(*ip); /* set up all the normal socket crap */ setsock(new_sock,(binary ? SOCK_BINARY : 0)); return new_sock; } /* like open_telnet, but uses server & port specifications of dcc */ int open_telnet_dcc(sock,server,port) int sock; char *server,*port; { int p; unsigned long addr; char sv[121]; unsigned char c[4]; if (port!=NULL) p=atoi(port); else p=2000; if (server!=NULL) addr=my_atoul(server); else addr=0L; if (addr < (1<<24)) return -3; /* fake address */ c[0]=(addr>>24)&0xff; c[1]=(addr>>16)&0xff; c[2]=(addr>>8)&0xff; c[3]=addr&0xff; sprintf(sv,"%u.%u.%u.%u",c[0],c[1],c[2],c[3]); /* strcpy(sv,hostnamefromip(addr)); */ p=open_telnet_raw(sock,sv,p); return p; } /* all new replacements for mtgets/mtread */ /* attempts to read from all the sockets in socklist */ /* fills s with up to 511 bytes if available, and returns the array index */ /* on EOF, returns -1, with socket in len */ /* on socket error, returns -2 */ /* if nothing is ready, returns -3 */ int sockread(s,len) char *s; int *len; { fd_set fd; int fds,i,x; struct timeval t; int grab=511; fds=getdtablesize(); #ifdef FD_SETSIZE if (fds>FD_SETSIZE) fds=FD_SETSIZE; /* fixes YET ANOTHER freebsd bug!!! */ #endif /* timeout: 1 sec */ t.tv_sec=1; t.tv_usec=0; FD_ZERO(&fd); for (i=0; i0) { /* something happened */ for (i=0; i 510) socklist[i].inbuf[510]=0; strcpy(s,socklist[i].inbuf); px=(char *)nmalloc(strlen(p+1)+1); strcpy(px,p+1); nfree(socklist[i].inbuf); if (px[0]) socklist[i].inbuf=px; else { nfree(px); socklist[i].inbuf=NULL; } if (s[strlen(s)-1]=='\r') s[strlen(s)-1]=0; *len = strlen(s); /* <-- oh that looks so cute robey! :) */ return socklist[i].sock; } } } /* no pent-up data of any worth -- down to business */ context; *len=0; ret=sockread(xx,len); if (ret<0) { s[0]=0; return ret; } /* binary and listening sockets don't get buffered */ if (socklist[ret].flags & SOCK_CONNECT) { socklist[ret].flags &= ~SOCK_CONNECT; s[0]=0; return socklist[ret].sock; } if (socklist[ret].flags & SOCK_BINARY) { my_memcpy(s,xx,*len); return socklist[ret].sock; } if (socklist[ret].flags & SOCK_LISTEN) return socklist[ret].sock; context; /* might be necessary to prepend stored-up data! */ if (socklist[ret].inbuf != NULL) { p=socklist[ret].inbuf; socklist[ret].inbuf=(char *)nmalloc(strlen(p)+strlen(xx)+1); strcpy(socklist[ret].inbuf,p); strcat(socklist[ret].inbuf,xx); nfree(p); if (strlen(socklist[ret].inbuf) < 512) { strcpy(xx,socklist[ret].inbuf); nfree(socklist[ret].inbuf); socklist[ret].inbuf=NULL; } else { p=socklist[ret].inbuf; socklist[ret].inbuf=(char *)nmalloc(strlen(p)-509); strcpy(socklist[ret].inbuf,p+510); *(p+510)=0; strcpy(xx,p); nfree(p); /* (leave the rest to be post-pended later) */ } } context; /* look for EOL marker; if it's there, i have something to show */ p=strchr(xx,'\n'); if (p!=NULL) { *p=0; strcpy(s,xx); strcpy(xx,p+1); if (s[strlen(s)-1]=='\r') s[strlen(s)-1]=0; if (!s[0]) strcpy(s," "); } else { s[0]=0; if (strlen(xx)>=510) { /* string is too long, so just insert fake \n */ strcpy(s,xx); xx[0]=0; } } context; *len = strlen(s); /* anything left that needs to be saved? */ if (!xx[0]) { if (s[0]) return socklist[ret].sock; else return -3; } context; /* prepend old data back */ if (socklist[ret].inbuf != NULL) { p=socklist[ret].inbuf; socklist[ret].inbuf=(char *)nmalloc(strlen(p)+strlen(xx)+1); strcpy(socklist[ret].inbuf,xx); strcat(socklist[ret].inbuf,p); nfree(p); } else { socklist[ret].inbuf=(char *)nmalloc(strlen(xx)+1); strcpy(socklist[ret].inbuf,xx); } if (s[0]) return socklist[ret].sock; else return -3; } /* dump something to a socket */ void tputs(z,s) int z; char *s; { int i,x; char *p; if (z<0) return; /* um... HELLO?! sanity check please! */ if (((z==STDOUT) || (z==STDERR)) && (!backgrd || use_stderr)) { write(z,s,strlen(s)); return; } for (i=0; i510) SBUF2[510]=0; /* server can only take so much */ tputs(sock,SBUF2); va_end(va); } /* DEBUGGING STUFF */ void tell_netdebug(idx) int idx; { int i; char s[80]; if (idx<0) tprintf(-idx,"Open sockets:"); else dprintf(idx,"Open sockets:"); for (i=0; i