Subversion Repositories oidinfo_api

Rev

Rev 3 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. // **************************************************
  2. // ** OID CSV Lookup Server 1.2                    **
  3. // ** (c) 2016-2020 ViaThinkSoft, Daniel Marschall **
  4. // **************************************************
  5.  
  6. // todo: log verbosity + datetime
  7. // todo: publish at codelib
  8. // todo: server hat sich einfach so beendet... "read: connection reset by peer"
  9. // todo: 2019-02-24 service was booted together with the system, and i got "0 OIDs loaded". why???
  10. // todo: create vnag monitor that checks if this service is OK
  11. // todo: unexplained crash with version 1.1 : Signal "pipefail" (141) received after someone connected?!
  12.  
  13. #include "oid_lookup_srv.h"
  14.  
  15. unordered_set<string> lines;
  16.  
  17. int loadfile(const string &filename) {
  18.         int cnt = 0;
  19.  
  20.         ifstream infile(filename);
  21.         string line;
  22.         while (getline(infile, line)) {
  23.                 lines.insert(line);
  24.                 ++cnt;
  25.         }
  26.         infile.close();
  27.  
  28.         fprintf(stdout, "Loaded %d OIDs from %s\n", cnt, filename.c_str());
  29.  
  30.         return cnt;
  31. }
  32.  
  33. bool stringAvailable(const string &str) {
  34.         return lines.find(str) != lines.end();
  35. }
  36.  
  37. // Template of this code: http://www.gnu.org/software/libc/manual/html_node/Server-Example.html
  38. //                        http://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example
  39.  
  40. struct con_descriptor {
  41.         time_t              last_activity;
  42.         struct sockaddr_in  clientname;
  43.         int                 queries;
  44.         time_t              connect_ts;
  45. };
  46.  
  47. con_descriptor cons[FD_SETSIZE];
  48.  
  49. int read_from_client(int filedes) {
  50.         char buffer[MAXMSG];
  51.         int nbytes;
  52.  
  53.         nbytes = read(filedes, buffer, sizeof(buffer));
  54.         buffer[sizeof(buffer)-1] = 0; // Terminator
  55.  
  56.         if (nbytes < 0) {
  57.                 /* Read error. */
  58.                 //perror("read");
  59.                 //exit(EXIT_FAILURE);
  60.                 return -1;
  61.         } else if (nbytes == 0) {
  62.                 /* End-of-file. */
  63.                 return -1;
  64.         } else {
  65.                 /* Data read. */
  66.  
  67.                 for (uint i=0; i<sizeof(buffer); ++i) {
  68.                         if ((buffer[i] == 13) || (buffer[i] == 10)) {
  69.                                 buffer[i] = 0; // Terminator
  70.                                 break;
  71.                         }
  72.                 }
  73.  
  74.                 if (strcmp(buffer, "bye") == 0) {
  75.                         fprintf(stdout, "%s:%d[%d] Client said good bye.\n", inet_ntoa(cons[filedes].clientname.sin_addr), ntohs(cons[filedes].clientname.sin_port), filedes);
  76.                         return -1;
  77.                 } else {
  78.                         cons[filedes].queries++;
  79.  
  80.                         for (uint i=0; i<sizeof(buffer); ++i) {
  81.                                 if (buffer[i] == 0) break;
  82.                                 if (!((buffer[i] >= '0') && (buffer[i] <= '9')) && !(buffer[i] == '.')) {
  83.                                         fprintf(stdout, "%s:%d[%d] Client sent an invalid request.\n", inet_ntoa(cons[filedes].clientname.sin_addr), ntohs(cons[filedes].clientname.sin_port), filedes);
  84.                                         return -1;
  85.                                 }
  86.                         }
  87.  
  88.                         // fprintf(stdout, "%s:%d[%d] Query #%d: %s\n", inet_ntoa(cons[filedes].clientname.sin_addr), ntohs(cons[filedes].clientname.sin_port), filedes, cons[filedes].queries, buffer);
  89.  
  90.                         if (stringAvailable(buffer)) {
  91.                                 write(filedes, "1\n", 2);
  92.                         } else {
  93.                                 write(filedes, "0\n", 2);
  94.                         }
  95.                         return 0;
  96.                 }
  97.         }
  98. }
  99.  
  100. int fd_set_isset_count(const fd_set &my_fd_set) {
  101.         int cnt = 0;
  102.         for (int fd = 0; fd < FD_SETSIZE; ++fd) {
  103.                 if (FD_ISSET(fd, &my_fd_set)) {
  104.                         ++cnt;
  105.                 }
  106.         }
  107.         return cnt;
  108. }
  109.  
  110. int loadCSVs() {
  111.         return loadfile("oid_table.csv");
  112. }
  113.  
  114. void initConsArray() {
  115.         for (int i=0; i<FD_SETSIZE; ++i) {
  116.                 cons[i].last_activity = 0;
  117.                 memset(&cons[i].clientname, 0, sizeof(sockaddr_in));
  118.                 cons[i].queries = 0;
  119.         }
  120. }
  121.  
  122. int make_socket(uint16_t port) {
  123.         int sock;
  124.         struct sockaddr_in name;
  125.  
  126.         /* Create the socket. */
  127.         sock = socket(PF_INET, SOCK_STREAM, 0);
  128.         if (sock < 0) {
  129.                 perror("socket");
  130.                 exit(EXIT_FAILURE);
  131.         }
  132.  
  133.         int enable = 1;
  134.         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
  135.                 fprintf(stderr, "ERROR: setsockopt(SO_REUSEADDR) failed");
  136.                 exit(EXIT_FAILURE);
  137.         }
  138.  
  139.         /* Give the socket a name. */
  140.         name.sin_family = AF_INET;
  141.         name.sin_port = htons (port);
  142.         name.sin_addr.s_addr = htonl (INADDR_ANY);
  143.         if (bind (sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
  144.                 perror("bind");
  145.                 exit(EXIT_FAILURE);
  146.         }
  147.  
  148.         return sock;
  149. }
  150.  
  151. int main(void) {
  152. //      extern int make_socket(uint16_t port);
  153.         int sock;
  154.         fd_set active_fd_set, read_fd_set;
  155.  
  156.         fprintf(stdout, "OID CSV Lookup Server 1.2 (c)2016-2020 ViaThinkSoft\n");
  157.         fprintf(stdout, "Listening at port: %d\n", PORT);
  158.         fprintf(stdout, "Max connections: %d\n", FD_SETSIZE);
  159.  
  160.         initConsArray();
  161.  
  162.         int loadedOIDs = loadCSVs();
  163.         time_t csvLoad = time(NULL);
  164.  
  165.         /* Create the socket and set it up to accept connections. */
  166.         sock = make_socket(PORT);
  167.         if (listen(sock, 1) < 0) {
  168.                 perror("listen");
  169.                 exit(EXIT_FAILURE);
  170.         }
  171.  
  172.         /* Initialize the set of active sockets. */
  173.         FD_ZERO(&active_fd_set);
  174.         FD_SET(sock, &active_fd_set);
  175.  
  176.         while (1) {
  177.                 /* Block until input arrives on one or more active sockets. */
  178.                 read_fd_set = active_fd_set;
  179.  
  180.                 struct timeval tv;
  181.                 tv.tv_sec = 1;
  182.                 tv.tv_usec = 0;  // Not init'ing this can cause strange errors
  183.  
  184.                 int retval = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &tv);
  185.  
  186.                 if (retval < 0) {
  187.                         perror("select");
  188.                         exit(EXIT_FAILURE);
  189.                 } else if (retval == 0) {
  190.                         // fprintf(stdout, "Nothing received\n");
  191.                 } else {
  192.                         /* Service all the sockets with input pending. */
  193.                         for (int i=0; i < FD_SETSIZE; ++i) {
  194.                                 if (FD_ISSET (i, &read_fd_set)) {
  195.                                         if (i == sock) {
  196.                                                 /* Connection request on original socket. */
  197.                                                 int new_fd;
  198.                                                 struct sockaddr_in clientname;
  199.                                                 socklen_t size = sizeof(clientname);
  200.                                                 new_fd = accept(sock, (struct sockaddr *) &clientname, &size);
  201.                                                 if (new_fd < 0) {
  202.                                                         perror("accept");
  203.                                                         exit(EXIT_FAILURE);
  204.                                                 }
  205.                                                 FD_SET(new_fd, &active_fd_set);
  206.  
  207.                                                 cons[new_fd].clientname = clientname;
  208.                                                 cons[new_fd].connect_ts = time(NULL);
  209.  
  210.                                                 if (loadedOIDs == 0) {
  211.                                                         loadedOIDs = loadCSVs();
  212.                                                         if (loadedOIDs == 0) {
  213.                                                                 fprintf(stderr, "%s:%d[%d] Service temporarily unavailable (OID list empty)\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), new_fd);
  214.                                                                 close(new_fd);
  215.                                                                 FD_CLR(new_fd, &active_fd_set);
  216.                                                         }
  217.                                                 }
  218.  
  219.                                                 if (fd_set_isset_count(active_fd_set)-1 > MAX_CONNECTIONS) { // -1 is because we need to exclude the listening socket (i=sock) which is not a connected client
  220.                                                         fprintf(stderr, "%s:%d[%d] Rejected because too many connections are open\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), new_fd);
  221.                                                         close(new_fd);
  222.                                                         FD_CLR(new_fd, &active_fd_set);
  223.                                                 } else {
  224.                                                         fprintf(stdout, "%s:%d[%d] Connected\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), new_fd);
  225.                                                 }
  226.  
  227.                                                 if (new_fd >= FD_SETSIZE) {
  228.                                                         fprintf(stderr, "%s:%d[%d] new_fd reached cons[FD_SETSIZE] limit\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), new_fd);
  229.                                                         close(new_fd);
  230.                                                         FD_CLR(new_fd, &active_fd_set);
  231.                                                 }
  232.  
  233.                                                 cons[new_fd].last_activity = time(NULL);
  234.                                                 cons[new_fd].queries = 0;
  235.                                         } else {
  236.                                                 /* Data arriving on an already-connected socket. */
  237.                                                 cons[i].last_activity = time(NULL);
  238.                                                 if (read_from_client(i) < 0) {
  239.                                                         fprintf(stdout, "%s:%d[%d] Connection closed after %d queries in %lu seconds.\n", inet_ntoa(cons[i].clientname.sin_addr), ntohs(cons[i].clientname.sin_port), i, cons[i].queries, time(NULL)-cons[i].connect_ts);
  240.                                                         close(i);
  241.                                                         FD_CLR(i, &active_fd_set);
  242.                                                 }
  243.                                         }
  244.                                 }
  245.                         }
  246.                 }
  247.  
  248.                 /* Check if we need to reload the CSV */
  249.                 if (time(NULL)-csvLoad >= CSVRELOADINTERVAL) {
  250.                         loadCSVs();
  251.                         csvLoad = time(NULL);
  252.                 }
  253.  
  254.                 /* Check if we can close connections due to timeout */
  255.                 for (int i=0; i < FD_SETSIZE; ++i) {
  256.                         if (FD_ISSET(i, &active_fd_set)) {
  257.                                 if (i == sock) continue;
  258.                                 if (time(NULL)-cons[i].last_activity >= CONNECTION_TIMEOUT) {
  259.                                         fprintf(stdout, "%s:%d[%d] Connection timeout.\n", inet_ntoa(cons[i].clientname.sin_addr), ntohs(cons[i].clientname.sin_port), i);
  260.                                         fprintf(stdout, "%s:%d[%d] Connection closed after %d queries in %lu seconds.\n", inet_ntoa(cons[i].clientname.sin_addr), ntohs(cons[i].clientname.sin_port), i, cons[i].queries, time(NULL)-cons[i].connect_ts);
  261.                                         close(i);
  262.                                         FD_CLR(i, &active_fd_set);
  263.                                 }
  264.                         }
  265.                 }
  266.         }
  267. }
  268.