Subversion Repositories oidinfo_api

Rev

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

Rev Author Line No. Line
3 daniel-mar 1
// **************************************************
14 daniel-mar 2
// ** OID CSV Lookup Server 1.2                    **
3
// ** (c) 2016-2020 ViaThinkSoft, Daniel Marschall **
3 daniel-mar 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
14 daniel-mar 11
// todo: unexplained crash with version 1.1 : Signal "pipefail" (141) received after someone connected?!
3 daniel-mar 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
 
14 daniel-mar 53
        nbytes = read(filedes, buffer, sizeof(buffer));
54
        buffer[sizeof(buffer)-1] = 0; // Terminator
55
 
3 daniel-mar 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
 
14 daniel-mar 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
                        }
3 daniel-mar 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
 
14 daniel-mar 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
 
3 daniel-mar 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
 
14 daniel-mar 156
        fprintf(stdout, "OID CSV Lookup Server 1.2 (c)2016-2020 ViaThinkSoft\n");
3 daniel-mar 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
 
14 daniel-mar 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
 
3 daniel-mar 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
}