/* * Milter client-side API, a library to talk to Milter filters * Supplementary functions to allow build without OpenFWTK * * (C) Copyright 2003 ADVA Research Center * (C) Copyright 2003 ArkanoiD */ #include "ci_milter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SO_LNONE 0 #define SO_LCRLF 1 #define SO_LCR 2 #define SO_LLF 3 int proxy_timeout = 300; void *xmalloc(size_t); void *xrealloc(void*,size_t); char *xstrdup(const char*); void sosetline(int); time_t sotimeout(int); int soread(int,char*,int); int sowrite(int,char*,int); int sogets(int,char*,int); int sosayn(int,char*,int); int sosay(int,char*); int conn_server(char*,int,int,char*); void addlist(char*,char***); void freelist(char***); int searchlist(char*,char**); int searchlistnc(char*,char**); void * xmalloc (request) size_t request; { void *p = malloc(request); if(!p) { syslog(LOG_ERR, "<%d>%.128s[%d]: FATAL: xalloc: request for %lu bytes failed %m", LOG_SYSLOG|LOG_INFO, __FILE__, getpid(), (unsigned long) request); abort(); } bzero(p, request); return p; } void * xrealloc (ptr,request) void* ptr; size_t request; { void *p = realloc(ptr,request); if(!p) { syslog(LOG_ERR, "<%d>%.128s[%d]: FATAL: xrealloc: request for %lu bytes failed %m", LOG_SYSLOG|LOG_INFO, __FILE__, getpid(), (unsigned long) request); abort(); } return p; } char* xstrdup(const char *str) { char *p = strdup(str); if(!p) { syslog(LOG_ERR, "<%d>%.128s[%d]: xstrdup failed %m", LOG_SYSLOG|LOG_INFO, __FILE__, getpid()); abort(); } return(p); } static struct timeval sotimo = { 300L, 0L }; static char crlf[2] = "\r\n"; int proxy_linestyle = SO_LCRLF; void sosetline(t) int t; { bzero(crlf, sizeof(crlf)); switch(t) { case SO_LNONE: break; case SO_LCRLF: (void)strcpy(crlf, "\r\n"); break; case SO_LCR: (void)strcpy(crlf, "\r"); break; case SO_LLF: (void)strcpy(crlf, "\n"); break; } proxy_linestyle = t; return; } time_t sotimeout(sec) int sec; { time_t oldtim = sotimo.tv_sec; if(sec) { sotimo.tv_sec = (sec == -1) ? 0L : (time_t)sec; sotimo.tv_usec = 0L; } return oldtim; } int soread(fd, buf, siz) int fd, siz; char *buf; { fd_set r; struct timeval timo; int x; if (!sotimo.tv_sec) sotimeout(300); timo = sotimo; while (1) { FD_ZERO(&r); FD_SET(fd, &r); x = select(fd + 1, &r, (fd_set *)0, (fd_set *)0, &timo); if (x == 0) { syslog(LOG_ERR, "Network read timeout signal after %lu seconds", sotimo.tv_sec); return(-1); } if (x < 0) { if (errno == EINTR) continue; return -1; } break; } x = read(fd, buf, siz); return((x == 0) ? -1 : x); } int sowrite(fd,s,n) int fd, n; char *s; { char *buffer; int x, nleft; fd_set w; struct timeval timo; buffer = s; nleft = n; if (!sotimo.tv_sec) sotimeout(300); timo = sotimo; while ( nleft > 0 ) { if ((x = write(fd, buffer, nleft)) <= 0 ) { if ( errno == EINTR ) x = 0; else if (errno == EAGAIN) { FD_ZERO(&w); FD_SET(fd, &w); switch(select(fd+1,NULL,&w,NULL,&timo)) { case -1: return(-1); case 0: syslog(LOG_ERR, "Network write timeout signal after %lu seconds", sotimo.tv_sec); return(-1); } } else return(-1); } nleft -= x; buffer += x; } return n; } int sogets(fd, buf, siz) int fd, siz; char *buf; { int x = 0; while(1) { if(soread(fd, (char *)&buf[x], 1) != 1) return(-1); /* some telnets send NULL-terminated lines in NOECHO :( */ if(buf[x] == '\0') continue; if(buf[x] == '\n') { buf[++x] = '\0'; return(x); } if(++x >= siz) { syslog(LOG_ERR, "fwtksyserr: sogets(): buffer overrun"); return(-1); } } } int sosayn(fd, s, n) int fd, n; char *s; { int tl = strlen(crlf); if(sowrite(fd, s, n) != n) return(1); return(tl ? sowrite(fd, crlf, tl) != tl : 0); } int sosay(fd, s) int fd; char *s; { return(sosayn(fd, s, strlen(s))); } int conn_server(srv,portnum,dummy,rbuf) char *srv; int portnum; int dummy; char *rbuf; { struct sockaddr_in addr; struct hostent *hp = 0; int fd; char *p; char **ap; p = srv; while(*p != '\0' && (*p == '.' || isdigit(*p))) p++; ap = 0; /* not all digits or dots */ if(*p != '\0') { if((hp = gethostbyname(srv)) == (struct hostent *)0) { if(rbuf != (char *)0) strcpy(rbuf,"hostname unknown"); return(-1); } ap = hp->h_addr_list; } else { unsigned long f; if((f = inet_addr(srv)) == -1L) { if(rbuf != (char *)0) strcpy(rbuf,"hostname unparsable"); return(-1); } (void)bcopy((char *)&f,(char *)&addr.sin_addr,sizeof(f)); } newaddr: if (hp) { if (hp->h_length > sizeof(addr.sin_addr.s_addr)) { syslog(LOG_ERR,"securityalert: invalid host address length (%d) hostname %.512s", hp->h_length, srv); strcpy(rbuf, "hostname invalid"); return (-1); } (void)bcopy(*ap,(char *)&addr.sin_addr,hp->h_length); } addr.sin_port = htons(portnum); addr.sin_family = AF_INET; fd = socket(AF_INET,SOCK_STREAM,0); if(fd < 0) { if(rbuf != (char *)0) sprintf(rbuf,"socket: %s",strerror(errno)); return(-2); } if(connect(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0) { if (hp && *++ap) goto newaddr; if(rbuf != (char *)0) sprintf(rbuf,"connect: %s",strerror(errno)); return(-3); } return(fd); } void addlist(entry,list) char *entry; char ***list; { int entries = 0; if (!*list) *list = (char **) xmalloc (sizeof(char *) *2); else { for (entries=0; (*list)[entries]; entries++) ; *list = (char **) xrealloc (*list,(entries+2)*sizeof(char*)); } if (!*list) return; (*list)[entries] = xstrdup(entry); (*list)[entries+1] = NULL; } void freelist(list) char ***list; { char **xp; if (!*list) return; for (xp = *list; *xp; xp++) free(*xp); free(*list); *list = NULL; } int searchlist(entry,list) char *entry; char **list; { char **xp; /* * Empty list means no restrictions and thus always matches */ if (!list) return(0); for (xp = list; *xp; xp++) { if (**xp == '!' && !strcmp(*xp + 1,entry)) return(1); if (!strcmp(*xp,entry) || !strcmp(*xp,"*")) return(0); } return(1); } int searchlistnc(entry,list) char *entry; char **list; { char **xp; /* * Empty list means no restrictions and thus always matches */ if (!list) return(0); for (xp = list; *xp; xp++) { if (**xp == '!' && !strcasecmp(*xp + 1,entry)) return(1); if(!strcasecmp(*xp,entry) || !strcasecmp(*xp,"*")) return(0); } return(1); }