#include #include #include #include "utils.h" extern char m_buffer[4096]; #define BANNER "220 " HOSTNAME " SMTP" #define HELOANSWER "250 " HOSTNAME #define CMDUNKNOWN "502 command unimplemented" #define DOMAINUNKNOWN "553 unknown domain" #define TEMPERROR "421 delivery error, please retry later" #define DEFERROR "521 permanent delivery error, do not retry" #define PORT 25 #define fail() do { perror(__FILE__ ":"); exit(-1); } while(0); struct serverinfo { char *address; unsigned port; }; unsigned long connid=0; struct serverinfo *get_server(char *domain) { char buffer[1024]; unsigned long *longs; /* connect to mysql and get values */ db=mysql_init(NULL); if(NULL==mysql_real_connect(db,DBHOST,DBUSER,DBPASS,DBNAME,0,NULL,0)) { fprintf(stderr,"%s\n",mysql_error(db)); mysql_close(db); return NULL; } /* clean string */ escape(db,domain); /* generate sql */ snprintf(buffer,sizeof buffer,"SELECT `host`,`port` FROM `domains` WHERE `domain`=\"%s\" ORDER BY RAND();",domain); /* query! */ if(mysql_query(db,buffer)) { fprintf(stderr,"%s\n",mysql_error(db)); mysql_close(db); return NULL; } for(i=0;row=mysql_fetch_row(db);i++) { longs=mysql_fetch_lengths(row); } mysql_close(db); return NULL; } char *get_domain(char *line) { char *strtmp=NULL; char *buf=NULL; safestrdup(buf,strchr(line,'@')+1); safestrdup(strtmp,strtok(buf," \"><")); condfree(buf); return strtmp; } void handle_new_connection(unsigned long p_connid,int fd) { char *buf=NULL; char *from=NULL; char *to=NULL; char *domain=NULL; char *serveraddress=NULL; int fdin=-1; int fds[2]; char databuffer[8192]; int bytesread; int fdread,fdwrite; struct serverinfo *server; connid=p_connid; write_string(fd,BANNER); while(!socketeof(fd)) { free_and_read_string(buf,fd); if(!cmp(buf,"HELO")||!cmp(buf,"EHLO")) write_string(fd,HELOANSWER); else if(!cmp(buf,"MAIL FROM")) { safestrdup(from,buf); write_string(fd,"250 Ok"); } else if(!cmp(buf,"RCPT TO")) { safestrdup(to,buf); domain=get_domain(to); if(NULL==(server=get_server(domain))) { write_string(fd,DOMAINUNKNOWN); exit(-1); } condfree(domain); break; } else write_string(fd,CMDUNKNOWN); } if(-1==(fdin=create_connected_socket(server->address,server->port))) { write_string(fd,TEMPERROR); exit(-1); } do /* manage errors as we would do an exception, all in one point */ { /* reproduce initial conversation on the new server */ free_and_read_string(buf,fdin); /* read banner */ write_string(fdin,"HELO " HOSTNAME); do { free_and_read_string(buf,fdin); } while('-'==buf[3]); write_string(fdin,from); do { free_and_read_string(buf,fdin); } while('-'==buf[3]); if(cmp(buf,"250")) { write_string(fd,DEFERROR); break; } write_string(fdin,to); /* now just copy data from one server to the other */ while(1) { fds[0]=fd; fds[1]=fdin; can_read(fds,2,-1); if(can_read(&fd,1,0)) { fdread=fd; fdwrite=fdin; } else { fdread=fdin; fdwrite=fd; } bytesread=read(fdread,databuffer,sizeof(databuffer)); if(bytesread<=0||write(fdwrite,databuffer,bytesread)<=0) { closeskt(fd); closeskt(fdin); break; } } } while(0); condfree(buf); condfree(to); condfree(from); closeskt(fd); closeskt(fdin); exit(0); } int main(int argc, char **argv) { int fd_server=-1,fd=-1; unsigned long conn=0; /* ignore dying child */ signal(SIGCHLD,SIG_IGN); openlog("dist",0,LOG_MAIL); if(-1==(fd_server=create_listening_socket(PORT))) fail(); while(-1!=(fd=accept(fd_server,NULL,0))) { debug("New connection!"); ++conn; switch(fork()) { case 0: closeskt(fd_server); handle_new_connection(conn,fd); case -1: fail(); default: closeskt(fd); } } close(fd_server); closelog(); exit(0); }