/* This program runs on hosts. It is contacted by the blackhole server and acts as a proxy for local vserver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fdpass.h" using namespace std; static DEBUG_KEY D_HORIZON("horizon","Horizon main program"); static DEBUG_KEY D_CONNECT("connect","new connections"); enum CONNECT_TYPE { TYPE_NONE, TYPE_CONTROL, TYPE_CLIENT, TYPE_MASTER, TYPE_MASTER_CONTROL, TYPE_FORK, TYPE_PROXY }; struct HANDLE_INFO: public ARRAY_OBJ{ CONNECT_TYPE type; string from; // Address of the connection or name of the vserver SSTRING comment; time_t lastping; // Last ping received from a master bool preferred; time_t intruder; struct { // Info to tell the master that the connection has ended string vserver; string port; int fd; } master; HANDLE_INFO(){ type = TYPE_NONE; lastping = (time_t)0; preferred = false; intruder = 0; master.fd = -1; } }; #include "proto/horizon_control.protoh" #include "proto/horizon_master.protoh" #include "proto/horizon_master_control.protoh" #include "proto/horizon_proxy.protoh" /* Copy back and forth between two raw tcp session. Ends whenever one hang up. */ static void horizon_runlink ( int fd1, int fd2, const char *description, int do_not_close) // This is a pipe used by the parent to learn when a connection is either moved to conproxy // or ended { if (fdpass_sendfd2proxy (fd1,fd2,do_not_close,description)==-1){ fdpass_loop (fd1,fd2,do_not_close); } _exit (0); } static void horizon_link (int fd1, int fd2, const char *description, int do_not_close) { if (fork()==0){ fdpass_okdata(fd1); horizon_runlink (fd1,fd2,description,do_not_close); _exit (0); } } static void horizon_connect ( int fd1, const char *clientbind, bool transparent_mode, const char *addr, const char *port, int link, const char *description, int do_not_close) { if (fork()==0){ int fd2; if (addr[0] == '/'){ fd2 = cmdsock_connect ("unix:",addr,5,1); if (fd2 == -1) tlmp_error ("Can't connect to unix socket %s (%s)\n",addr,strerror(errno)); }else{ fd2 = fdpass_tcpconnect (clientbind,transparent_mode,addr,port); } debug_printf (D_HORIZON,"horizon_connect addr=%s port=%s fd2=%d (%s)\n",addr,port,fd2,strerror(errno)); if (fd2 != -1){ if (link != -1){ close (fd1); fd1 = link; }else{ // This tells the blackhole we are ready to receive data on this handle fdpass_okdata(fd1); } horizon_runlink (fd1,fd2,description,do_not_close); } _exit (0); } } static void horizon_remove (vector &v, int fd) { for (vector::iterator it = v.begin(); it != v.end(); it++){ if (*it == fd){ v.erase(it); break; } } } static int horizon_getsockname(int fd, struct sockaddr *sa, unsigned *salen) { int ret = -1; if (*salen != sizeof(struct sockaddr_in)) { errno = EINVAL; }else{ if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, sa, salen) != -1) { ret = 0; }else if (getsockname(fd, sa, salen) != -1){ ret = 0; } } return ret; } struct OBJCON{ const char *vserver; const char *port; const char *linkstr; int logid; const char *key1; const char *key2; const char *line; int no; bool endclient; }; struct LISTEN_INFO{ string bindaddr; string port; LISTEN_INFO(const char *_bindaddr, const char *_port){ bindaddr = _bindaddr; port = _port; } LISTEN_INFO(const string &_bindaddr, const string &_port){ bindaddr = _bindaddr; port = _port; } bool operator < (const LISTEN_INFO &n) const { bool ret = false; if (bindaddr < n.bindaddr){ ret = true; }else if (bindaddr == n.bindaddr){ ret = port < n.port; } return ret; } }; /* Remove an entry in a map */ static void horizon_remove (map &tb, const char *name) { map::iterator it = tb.find(name); if (it != tb.end()){ tb.erase(it); } } /* Same as horizon_remove, but the key is the value */ static void horizon_remove2 (map &tb, const char *name) { for (map::iterator it = tb.begin(); it != tb.end(); it++){ if (strcmp(it->second.c_str(),name)==0){ tb.erase(it); break; } } } int main (int argc, char *argv[]) { glocal const char *clientbind = NULL; glocal const char *pidfile = "/run/horizon.pid"; glocal const char *user = "blackhole"; glocal bool daemon = false; glocal vector masterbinds; glocal const char *control = "/var/run/blackhole/horizon.sock"; glocal vector binds; glocal vector proxys; glocal bool open_network = false; glocal bool open_client = false; glocal const char *findprocport = "/var/run/findproc.sock"; glocal bool findproc = false; glocal string lasterror; glocal int nbping = 0; // Number of ping received so far glocal int lasterror_ping = 0; // Used to reset the lasterror // lasterror is transmitted for a while to the blackhole // and then resets itself. glocal const char *secretfile = NULL; glocal int maxhandles = 3000; // 1/3 of what conproxy handles by default glocal int ret; glocal const char *errorfile = NULL; glocal FILE *ferror = NULL; glocal.masterbinds.push_back ("0.0.0.0,8000"); glocal.ret = (argc,argv); setproginfo ("horizon",VERSION,"Forward TCP request from services on host\n" "the blackhole server\n"); setgrouparg ("Port"); setarg (' ',"bind","Bind on IP,port",glocal.binds,false); setarg (' ',"control","Unix socket control port",glocal.control,false); setarg (' ',"proxy","Bind on IP,port, proxy mode",glocal.proxys,false); setarg (' ',"secretfile","File holding secrets",glocal.secretfile,false); fdpass_setarg (this); setarg (' ',"master","Listen for blackhole server on IP,port or unix socket",glocal.masterbinds,false); setarg (' ',"open_network","Allow connection from outside network",glocal.open_network,false); setarg (' ',"open_client","Allow connection to outside network",glocal.open_client,false); setarg (' ',"clientbind","Bind on this IP when connecting",glocal.clientbind,false); setarg (' ',"findprocport","Unix socket of the findproc daemon",glocal.findprocport,false); setarg (' ',"findproc","Use the findproc daemon",glocal.findproc,false); setgrouparg ("Daemon options"); setarg (' ',"daemon","Run in background",glocal.daemon,false); setarg (' ',"pidfile","Write the daemon pid in this file",glocal.pidfile,false); setarg (' ',"user","Run as this user",glocal.user,false); setgrouparg ("Misc."); setarg (' ',"maxhandles","Maximum number of file handle supported",glocal.maxhandles,false); setarg (' ',"errorfile","Error message will be written to this file",glocal.errorfile,false); glocal.lasterror = msg; // Make sure bad characters (protocol) are turned to blank // because the lasterror is transmitted to the masters for (unsigned i=0; i if (glocal.ferror != NULL){ fprintf (stderr,"WARN: %s",msg); }else if (glocal.daemon){ syslog (LOG_WARNING,"%s",msg); }else{ fprintf (stderr,"%s",msg); } int ret = -1; glocal string findprocportbuf = glocal.findprocport; // If findprocport is redefined (see f findprocport // this will be used as the buffer glocal vector masters; // Masters handle use to request connection glocal map ipnames; // Logical names of the bound address of the listening sockets glocal set master_ports; glocal map ip2v; // Find a vserver from an ip glocal map v2ip; // Find the ip of a vserver glocal map vrootdir; // root directory of a vserver, for unix sockets glocal map n_ip2v; // temp for ip2v et v2ip to update without interruption glocal map n_v2ip; glocal map n_vrootdir; glocal map pendings; // Client connection waiting to be linked glocal set allows; // Allow master connection from those IPs glocal int nbfork = 0; glocal set proxyfds; // Handle for proxy listening sockets glocal string mysecret; glocal int maxconnect=0; // Maximum number of concurent connection (file handles) glocal map listens; // Port on which we are currently listening fdpass_readsecret (glocal.secretfile,glocal.mysecret); fdpass_checkservice (glocal.control); if (glocal.errorfile != NULL){ glocal.ferror = fopen (glocal.errorfile,"a"); if (glocal.ferror == NULL){ tlmp_error ("Can't open errorfile %s (%s), aborting\n",glocal.errorfile,strerror(errno)); exit (-1); } } (); debug_printf (D_HORIZON,"newclient no=%d port=%s from=%lx litenhandle=%d\n",no,info.port,from,info.listenhandle); HANDLE_INFO *c = new HANDLE_INFO; info.data = c; char addr[20]; ipnum_ip2a (from,addr); c->from = addr; if (glocal.master_ports.count(info.port)>0){ // This is a connection from a blackhole (a master) // This can be either a TCP or Unix connection /* The first connection from the blackhole is the way to talk back with the blackhole. The following connections are used to establish a connection with another horizon server. Only the blackhole can connect to an horizon server. */ c->lastping = time(nullptr); if (strncmp(info.port,"unix:",5) == 0){ c->type = TYPE_MASTER; c->from = info.port; }else if (glocal.allows.count(addr)==0){ endclient = true; tlmp_error ("Rejected master connection from ip %s\n",addr); }else{ // A connection is either made by a blackhole or a wormhole. // A connection starts as a master and then becomes a MASTER_CONTROL // if the master command is received. c->type = TYPE_MASTER; } settcpnodelay(true); }else if (strncmp(info.port,"unix:",5)==0){ c->from = "unix:"; c->type = TYPE_CONTROL; }else{ time_t now = time(NULL); if (glocal.masters.size() == 0){ tlmp_error ("No master connected yet, connection from %s on port %s rejected\n" ,addr,info.port); endclient = true; }else if (glocal.proxyfds.count(info.listenhandle)>0){ // This is a proxy connection. We wait until we get a connect line // Once we have received the connect line, we can confirm and then stop // listening on the handle since it becomes a data handle. c->type = TYPE_PROXY; map::iterator it = glocal.ip2v.find (addr); if (it == glocal.ip2v.end()){ tlmp_error ("Proxy connection from unknown source (vserver) %s\n",addr); endclient = true; }else{ debug_printf (D_HORIZON,"Proxy cLient connect vserver=%s port=%s\n",it->second.c_str() ,info.port); c->comment.setfromf ("proxy %s %s",it->second.c_str(),info.port); c->from = it->second; glocal.pendings[no] = now; } }else{ // Finds the target of the connection. This may be different from the // address bound on the socket in case of kernel iptable redirection // allowing the horizon server to act as a transparent proxy string dstname,dstaddr; string dstport = info.port; string connect_from; if (info.port[0] == '/'){ for (map::iterator it=glocal.listens.begin(); it != glocal.listens.end(); it++){ if (it->second == info.listenhandle){ dstname = "unix"; connect_from = glocal.ipnames[it->first.bindaddr]; break; } } }else{ struct sockaddr_in adr; unsigned int len = sizeof(adr); if (horizon_getsockname (no,(struct sockaddr*)&adr,&len) == -1){ tlmp_error ("getsockname fail (%s)\n",strerror(errno)); dstname = "unknown"; }else{ unsigned daddr = ntohl(adr.sin_addr.s_addr); char tmp_dstaddr[20]; ipnum_ip2a (daddr,tmp_dstaddr); dstaddr = tmp_dstaddr; /* Nice trick here. We extract the destination port of the connected socket. Normally, this is the same port number as info.port (which is the port we used in the listen syscall). But if we use iptable REDIRECT, it is possible to intercept the complete TCP traffic and send everything to this horizon socket. The kernel keeps the original destination port. Using a single listening socket and a single iptable line, we can control all the outgoing traffic of vservers. Blackhole is then used to route this traffic or reject it. */ unsigned dport = ntohs(adr.sin_port); char port[20]; snprintf (port,sizeof(port),"%u",dport); dstport = port; debug_printf (D_HORIZON,"getsockname %s\n",tmp_dstaddr); dstname = dstaddr; // Finds the connection logical name map::iterator it = glocal.ipnames.find(tmp_dstaddr); if (it != glocal.ipnames.end()){ dstname = it->second; } debug_printf (D_HORIZON,"New connection to %s -> %s\n",tmp_dstaddr,dstname.c_str()); } map::iterator it = glocal.ip2v.find (addr); if (it != glocal.ip2v.end()) connect_from = it->second; } // We are using the first master in glocal.masters, since the last ping received // ends up in front. See the ping command below. int master_handle = glocal.masters[0]; c->type = TYPE_CLIENT; setrawmode (true); if (connect_from.size()==0){ if (glocal.open_network){ debug_printf (D_HORIZON,"CLient connect open_network=%s port=%s\n",addr ,dstport.c_str()); c->comment.setfromf ("%s %s %s",addr,dstname.c_str(),dstport.c_str()); fdpass_sendtof (master_handle,glocal.mysecret,"connect %s %s %s %d",addr,dstname.c_str() ,dstport.c_str(),no); glocal.pendings[no] = now; }else{ tlmp_error ("Connection from unknown source (vserver) %s\n",addr); endclient = true; } }else{ if (glocal.findproc){ string command,user,group,xid,env; char from_port[20]; snprintf (from_port,sizeof(from_port)-1,"%u",info.source_port); if (fdpass_findproc (glocal.findprocport,addr,from_port,dstaddr.c_str(),dstport.c_str() ,command,user,group,xid,env)!=-1){ connect_from += "," + command + "," + user + "," + group + "," +env + "," + xid; } } debug_printf (D_HORIZON,"CLient connect vserver=%s port=%s\n",connect_from.c_str() ,dstport.c_str()); c->comment.setfromf ("%s %s %s",connect_from.c_str(),dstname.c_str(),dstport.c_str()); fdpass_sendtof (master_handle,glocal.mysecret,"connect %s %s %s %d",connect_from.c_str() ,dstname.c_str(),dstport.c_str(),no); glocal.pendings[no] = now; } setlisten (no,false); // Do not read anything from this socket until // we fork debug_printf(D_CONNECT,"newclient type=%d fd=%d port=%s connect_from=%s\n",c->type,no,info.port,connect_from.c_str()); } } int nbc = getnbclients(); if (nbc > glocal.maxconnect) glocal.maxconnect = nbc; HANDLE_INFO *c = (HANDLE_INFO*)info.data; if (c->type == TYPE_MASTER_CONTROL){ debug_printf (D_HORIZON,"master %d has disconnnected\n",no); horizon_remove (glocal.masters,no); tlmp_warning ("One master-control has disconnected, %zu remaining\n",glocal.masters.size()); }else if (c->type == TYPE_FORK){ debug_printf (D_HORIZON,"sub-process ending: %s\n",c->comment.c_str()); if (c->master.fd != -1){ debug_printf (D_HORIZON,"sending endcon %s %s\n" ,c->master.vserver.c_str(),c->master.port.c_str()); fdpass_sendtof (c->master.fd,glocal.mysecret,"endcon %s %s" ,c->master.vserver.c_str(),c->master.port.c_str()); } glocal.nbfork--; } if (glocal.pendings.find(no) != glocal.pendings.end()){ // This situation is not possible because we are not listening on pending connection // at all, so we are even missing the close. We only see the close when we // actually start to listen (when we receive the link or connect command back // from the blackhole // See the testrace program tlmp_error ("Closing a pending connection %d, impossible\n",no); glocal.pendings.erase(no); } if (c->type == TYPE_MASTER || c->type == TYPE_MASTER_CONTROL){ void *data; int fd = iter_init(data); int nb = 0; while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_FORK && n->master.fd == no){ n->master.fd = -1; nb++; } fd = iter_next(data); } if (nb > 0) tlmp_warning ("A master blackhole has disconnected: %d connections won't be accounted\n",nb); } glocal OBJECTSUB *objcon; glocal OBJCON objconparms; glocal int fd = no; // connection request may come from the 2 master connection types // so the common code is placed here (); glocal.objconparms.endclient = false; const char *linkstr = glocal.objconparms.linkstr; const char *vserver = glocal.objconparms.vserver; const char *port = glocal.objconparms.port; int no = glocal.objconparms.no; const char *line = glocal.objconparms.line; int link = isdigit(linkstr[0]) ? atoi(linkstr) : -1; const char *remote = strchr(vserver,'/'); string tmp_vserver; if (remote != NULL){ tmp_vserver = string(vserver,remote-vserver); vserver = tmp_vserver.c_str(); remote++; } string tmpaddr; // Buffer to assemble the unix socket complete path const char *addr = NULL; const char *cbind = glocal.clientbind; bool transparent_mode = false; // Are we connecting to a unix socket if (port[0] == '/'){ map::iterator it = glocal.vrootdir.find(vserver); if (it != glocal.vrootdir.end()){ tmpaddr = it->second + port; addr = tmpaddr.c_str(); }else{ tlmp_error ("No vserverdir definition for vserver %s, can't connect to unix socket %s\n" ,vserver,port); } }else{ map::iterator it = glocal.v2ip.find(vserver); if (it != glocal.v2ip.end()){ addr = it->second.c_str(); if (remote != NULL){ map::iterator v = glocal.ip2v.find(remote); if (v == glocal.ip2v.end()){ // This is not an IP of a vservers // so we assume this is not an IP of this server transparent_mode = true; } cbind = remote; } }else if (glocal.open_client){ addr = vserver; cbind = remote; } } if (link != -1 && glocal.pendings.find(link) == glocal.pendings.end()){ tlmp_error ("Rejected connect/link command from master: %s\n",line); glocal.objconparms.endclient = true; }else if (addr == NULL){ tlmp_error ("Can't find vserver: vserver=%s port=%s line=%s\n",vserver,port,line); glocal.objconparms.endclient = true; if (link != -1){ glocal.TCPSERVER.closeclient (link); glocal.pendings.erase(link); } }else{ glocal.pendings.erase(link); int tb[2]; if (pipe(tb)==-1){ tlmp_error ("connect: Can't set pipe (%s)\n",strerror(errno)); glocal.objconparms.endclient = true; }else{ HANDLE_INFO *n = new HANDLE_INFO; n->type = TYPE_FORK; n->master.vserver = glocal.objconparms.key1; n->master.port = glocal.objconparms.key2; if (strcmp(glocal.objconparms.key1,"-")==0){ n->master.fd = -1; }else{ n->master.fd = glocal.fd; } const char *linkinfo = "remote"; if (link != -1){ HANDLE_INFO *nfd = (HANDLE_INFO*)glocal.TCPSERVER.getclientdata(link); linkinfo = nfd->comment.c_str(); } if (glocal.objconparms.logid != -1){ n->comment.setfromf("LOGID=%d ",glocal.objconparms.logid); } n->comment.appendf ("connect %s %s %s: %s",vserver,port,linkstr,linkinfo); glocal.TCPSERVER.inject (tb[0],n); glocal.nbfork++; horizon_connect (no,cbind,transparent_mode,addr,port,link,n->comment.c_str(),tb[1]); glocal.objconparms.endclient=true; close (tb[1]); if (link != -1){ glocal.TCPSERVER.closeclient (link); } int nbc = glocal.TCPSERVER.getnbclients(); if (nbc > glocal.maxconnect) glocal.maxconnect = nbc; } } glocal.objcon = &objcon; //debug_printf (D_HORIZON,"receive %d: %s\n",no,line); HANDLE_INFO *c = (HANDLE_INFO*)info.data; if (c->type == TYPE_CLIENT){ tlmp_error ("Not possible, receive from CLIENT %d\n",info.linelen); }else if (c->type == TYPE_CONTROL){ (this,line,endserver,endclient,no,c); horizon_remove2 (glocal.ip2v,name); if (strcmp(addr,"-")==0){ horizon_remove (glocal.v2ip,name); }else{ glocal.ip2v[addr] = name; glocal.v2ip[name] = addr; } debug_printf (D_HORIZON,"Vserver definition: %s %s\n",name,addr); if (strcmp(rootdir,"-")==0){ horizon_remove (glocal.vrootdir,name); }else{ glocal.vrootdir[name] = rootdir; } debug_printf (D_HORIZON,"Vserver directory: %s %s\n",name,rootdir); // The n_commands (n_vserver,n_clear,n_commit) were created to update the // vserver definitions without causing service interruption or problems. // You use the sequence: n_clear, n_vserver, n_vserver, ..., n_commit glocal.n_ip2v[addr] = name; glocal.n_v2ip[name] = addr; debug_printf (D_HORIZON,"n_vserver definition: %s %s\n",name,addr); glocal.n_vrootdir[name] = rootdir; debug_printf (D_HORIZON,"n_vserver directory: %s %s\n",name,rootdir); glocal.n_ip2v.clear(); glocal.n_v2ip.clear(); glocal.n_vrootdir.clear(); debug_printf (D_HORIZON,"n_Vserver clear\n"); glocal.ip2v = glocal.n_ip2v; glocal.v2ip = glocal.n_v2ip; glocal.vrootdir = glocal.n_vrootdir; debug_printf (D_HORIZON,"n_Vserver commit\n"); debug_printf (D_HORIZON,"Nameip definition: %s %s\n",ip,name); glocal.ipnames[ip] = name; glocal.allows.insert (addr); debug_printf (D_HORIZON,"Allow master connection from ip %s\n",addr); // Check if we are already listening on that ip,port combination LISTEN_INFO listen(ip,port); if (strcmp(ip,"unix")==0){ listen.bindaddr = string_f("%s/%s",ip,name); } map::iterator it = glocal.listens.find(listen); if (it == glocal.listens.end()){ glocal.TCPSERVER.send ("waiting\n"); int fd = fdpass_receivefd(no); if (fd == -1){ tlmp_error ("Can't receive a new socket\n"); }else{ glocal.TCPSERVER.listenfd (fd,port); glocal.listens[listen] = fd; glocal.ipnames[listen.bindaddr] = name; } }else{ glocal.TCPSERVER.sendf ("Already listening on %s port %s\n",ip,port); glocal.ipnames[ip] = name; } LISTEN_INFO listen(ip,port); if (strcmp(ip,"unix")==0){ listen.bindaddr = string_f("%s/%s",ip,name); } map::iterator it = glocal.listens.find(listen); if (it == glocal.listens.end()){ glocal.TCPSERVER.sendf ("Not listening on %s port %s\n",ip,port); }else{ glocal.TCPSERVER.unlisten(it->second); glocal.listens.erase (it); } fdpass_readsecret (glocal.secretfile,glocal.mysecret); glocal.findproc = strcasecmp(on,"on")==0; glocal.findprocportbuf = port; glocal.findprocport = glocal.findprocportbuf.c_str(); glocal.TCPSERVER.sendf ("horizon version %s\n",VERSION); glocal.TCPSERVER.sendf ("maxhandles: %d\n",glocal.maxhandles); glocal.TCPSERVER.sendf ("max connections: %d\n",glocal.maxconnect); glocal.TCPSERVER.sendf ("master=%zu connected\n",glocal.masters.size()); glocal.TCPSERVER.sendf ("nbfork=%d\n",glocal.nbfork); glocal.TCPSERVER.sendf ("lasterror=%s\n",glocal.lasterror.c_str()); glocal.TCPSERVER.sendf ("findproc=%d findprocport=%s\n",glocal.findproc,glocal.findprocport); for (map::iterator it=glocal.ip2v.begin(); it != glocal.ip2v.end(); it++){ glocal.TCPSERVER.sendf ("vserver %s %s\n",it->second.c_str(),it->first.c_str()); } for (map::iterator it=glocal.vrootdir.begin(); it != glocal.vrootdir.end(); it++){ glocal.TCPSERVER.sendf ("vserverdir %s %s\n",it->first.c_str(),it->second.c_str()); } for (map::iterator it=glocal.ipnames.begin(); it != glocal.ipnames.end(); it++){ glocal.TCPSERVER.sendf ("nameip %s %s\n",it->first.c_str(),it->second.c_str()); } for (set::iterator it=glocal.allows.begin(); it != glocal.allows.end(); it++){ glocal.TCPSERVER.sendf ("allow %s\n",it->c_str()); } glocal.TCPSERVER.sendf ("pendings=%zu\n",glocal.pendings.size()); for (map::iterator it = glocal.listens.begin(); it != glocal.listens.end(); it++){ string &name = glocal.ipnames[it->first.bindaddr]; glocal.TCPSERVER.sendf ("listen %s %s %s [%d]\n" ,name.c_str(),it->first.bindaddr.c_str(),it->first.port.c_str(),it->second); } void *data; int fd = glocal.TCPSERVER.iter_init(data); time_t now = time(NULL); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; glocal.TCPSERVER.sendf ("Connection %d %s",fd,n->from.c_str()); if (n->type == TYPE_CONTROL){ glocal.TCPSERVER.send ("(control)"); }else if (n->type == TYPE_MASTER_CONTROL){ char intruder[100]; fdpass_format_intruder (n->intruder,intruder); glocal.TCPSERVER.sendf ("(MASTER last_ping %lds ago %s) %s",now-n->lastping ,n->preferred ? "preferred" : "",intruder); }else if (n->type == TYPE_MASTER){ char intruder[100]; fdpass_format_intruder (n->intruder,intruder); glocal.TCPSERVER.sendf ("(master) %s",intruder); }else if (n->type == TYPE_FORK){ glocal.TCPSERVER.send ("(fork)"); }else if (n->type == TYPE_CLIENT){ glocal.TCPSERVER.send ("(pending)"); }else if (n->type == TYPE_PROXY){ glocal.TCPSERVER.send ("(proxy)"); } glocal.TCPSERVER.sendf (": %s\n",n->comment.c_str()); fd = glocal.TCPSERVER.iter_next(data); } void *data; int fd = glocal.TCPSERVER.iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_FORK){ glocal.TCPSERVER.sendf ("fd=%d %s\n",fd,n->comment.c_str()); } fd = glocal.TCPSERVER.iter_next(data); } int n = atoi(fd); glocal.TCPSERVER.closeclient(n); glocal.pendings.erase(n); glocal.lasterror.clear(); glocal.maxconnect = glocal.TCPSERVER.getnbclients(); tlmp_warning ("Endserver requested\n"); endserver = true; bool ison = atoi(on)==1; if (ison){ debug_seton(); }else{ debug_setoff(); } debug_setfdebug (filename); glocal.TCPSERVER.send ("Invalid command\n"); endclient = true; }else if (c->type == TYPE_MASTER_CONTROL){ (this,line,endserver,endclient,glocal.mysecret,c->intruder,no,c); debug_printf (D_EXTRA,"Ping from master %d\n",no); if (c->preferred){ horizon_remove (glocal.masters,no); glocal.masters.insert(glocal.masters.begin(),no); } c->lastping = time(NULL); fdpass_sendf (&glocal.TCPSERVER,glocal.mysecret,"pong %s",glocal.lasterror.c_str()); glocal.nbping++; if (glocal.nbping - glocal.lasterror_ping > 5*12) glocal.lasterror.clear(); // If the blackhole is overloaded (too many connections), it may fails to handle // some connection requests and some pendings may live here forever time_t oldtime = time(NULL)-60; size_t pending_size = glocal.pendings.size(); for (map::iterator it=glocal.pendings.begin(); it != glocal.pendings.end() ; ){ if (it->second < oldtime){ glocal.TCPSERVER.closeclient(it->first); map::iterator old = it; it++; glocal.pendings.erase(old); }else{ it++; } } size_t diff = pending_size - glocal.pendings.size(); if (diff != 0){ tlmp_error ("Closing %zu pending connections\n",diff); } int fd = atoi(fdstr); debug_printf (D_HORIZON,"Reject connection handle %d\n",fd); if (glocal.pendings.find(fd) != glocal.pendings.end()){ glocal.TCPSERVER.closeclient (fd); glocal.pendings.erase(fd); }else{ tlmp_error ("blackhole rejected unknown connection: link=%s\n",fdstr); } glocal.objconparms.vserver = vserver; glocal.objconparms.port = port; glocal.objconparms.linkstr = linkstr; glocal.objconparms.logid = atoi(logid); glocal.objconparms.key1 = key1; glocal.objconparms.key2 = key2; glocal.objconparms.no = no; glocal.objconparms.line = line; glocal.objcon->exec(); glocal.TCPSERVER.send ("Invalid command\n"); tlmp_error ("Rejected command from master-control: %s\n",line); }else if (c->type == TYPE_MASTER){ (this,line,endserver,endclient,glocal.mysecret,c->intruder,no,c); if (strcmp(version,PROTOCOL_VERSION)!=0){ tlmp_error ("Rejected master, expected protocol version %s, got %s\n" ,PROTOCOL_VERSION,version); }else{ c->type = TYPE_MASTER_CONTROL; // The preferred masters are always put at the front (see ping above) c->preferred = strcmp(nearfar,"near")==0; debug_printf (D_HORIZON,"master is now handle %d (preferred=%d)\n",no,c->preferred); if (c->preferred){ glocal.masters.insert(glocal.masters.begin(),no); }else{ glocal.masters.push_back(no); } } int fd = atoi(fdstr); debug_printf (D_HORIZON,"Reject connection handle %d\n",fd); if (glocal.pendings.find(fd) != glocal.pendings.end()){ glocal.TCPSERVER.closeclient (fd); glocal.pendings.erase(fd); }else{ tlmp_error ("blackhole rejected unknown connection: link=%s\n",fdstr); } int fd = atoi(fdstr); if (glocal.pendings.find(fd) == glocal.pendings.end()){ tlmp_error ("Rejected link command from master: %s\n",line); endclient = true; }else{ glocal.pendings.erase(fd); int tb[2]; if (pipe(tb)==-1){ tlmp_error ("link: Can't set pipe (%s)\n",strerror(errno)); endclient = true; }else{ // We set a pipe to count the forked process. // When the process end, the pipe close and endclient // is called. HANDLE_INFO *n = new HANDLE_INFO; n->type = TYPE_FORK; HANDLE_INFO *nfd = (HANDLE_INFO*)glocal.TCPSERVER.getclientdata(fd); n->comment.setfromf ("link %d: %s",fd,nfd->comment.c_str()); glocal.TCPSERVER.inject (tb[0],n); glocal.nbfork++; horizon_link (no,fd,n->comment.c_str(),tb[1]); endclient=true; glocal.TCPSERVER.closeclient (fd); close (tb[1]); int nbc = glocal.TCPSERVER.getnbclients(); if (nbc > glocal.maxconnect) glocal.maxconnect = nbc; } } glocal.objconparms.vserver = vserver; glocal.objconparms.port = port; glocal.objconparms.linkstr = linkstr; glocal.objconparms.logid = atoi(logid); glocal.objconparms.key1 = key2; glocal.objconparms.key2 = key2; glocal.objconparms.no = no; glocal.objconparms.line = line; glocal.objcon->exec(); endclient = glocal.objconparms.endclient; glocal.TCPSERVER.send ("Invalid command\n"); tlmp_error ("Rejected command from master: %s\n",line); endclient = true; }else if (c->type == TYPE_PROXY){ (this,line,endserver,endclient,no,c); int master_handle = glocal.masters[0]; fdpass_sendtof (master_handle,glocal.mysecret,"connect %s %s %s %d",c->from.c_str() ,addr,port,no); glocal.TCPSERVER.setrawmode (true); glocal.TCPSERVER.setlisten (no,false); c->type = TYPE_CLIENT; tlmp_error ("Invalid proxy command: %s\n",line); endclient = true; int nbc = glocal.TCPSERVER.getnbclients(); if (nbc > glocal.maxconnect) glocal.maxconnect = nbc; } bool some_errors = false; for (unsigned i=0; i::iterator it = glocal.ipnames.find(ip); if (it == glocal.ipnames.end()){ char tmp[20]; snprintf (tmp,sizeof(tmp),"ip%d",namealloc); glocal.ipnames[ip] = tmp; namealloc++; } } } } } { for (unsigned i=0; i return glocal.ret; }