+/* --- @check__client@ --- *
+ *
+ * Arguments: @request *rq@ = pointer to a request block
+ * @FILE *fp@ = file containing server configuration
+ *
+ * Returns: Nonzero if OK, zero if forbidden
+ *
+ * Use: Asks one or several servers whether a request is acceptable.
+ */
+
+int check__client(request *rq, FILE *fp)
+{
+ /* --- Format of the file --- *
+ *
+ * The `servers' file contains entries of the form
+ *
+ * %%\syntax{<host> [`:' <port>]}%%
+ *
+ * separates by whitespace. I build them all into an array of socket
+ * addresses and pass the whole lot to another function.
+ */
+
+ struct sockaddr_in *serv; /* Array of servers */
+ size_t n_serv, max_serv; /* Number and maximum number */
+
+ /* --- Initialise the server array --- */
+
+ T( trace(TRACE_CLIENT, "client: reading server definitions"); )
+ n_serv = 0; max_serv = 4; /* Four seems reasonable */
+ serv = xmalloc(sizeof(*serv) * max_serv);
+
+ /* --- Start reading the file --- */
+
+ {
+ char buff[256], *p, *l; /* A buffer and pointers for it */
+ int port; /* Default port for servers */
+ int state; /* Current parser state */
+ struct in_addr t_host; /* Temp place for an address*/
+ int t_port; /* Temp place for a port */
+ int ch; /* The current character */
+
+ /* --- Parser states --- */
+
+ enum {
+ st_start, /* Waiting to begin */
+ st_host, /* Reading a new hostname */
+ st_colon, /* Expecting a colon, maybe */
+ st_preport, /* Waiting before reading port */
+ st_port, /* Reading a port number */
+ st_commit, /* Commit a newly read server */
+ st_done /* Finished reading the file */
+ };
+
+ /* --- Find a default port --- */
+
+ {
+ struct servent *s = getservbyname(quis(), "udp");
+ port = (s ? s->s_port : -1);
+ }
+
+ /* --- Initialise for scanning the file --- */
+
+ state = st_host;
+ p = buff;
+ l = buff + sizeof(buff);
+ t_port = port;
+ ch = getc(fp);
+
+ /* --- Go for it --- */
+
+ while (state != st_done) {
+ switch (state) {
+
+ /* --- Initial whitespace before hostname --- */
+
+ case st_start:
+ if (ch == EOF)
+ state = st_done;
+ else if (isspace((unsigned char)ch))
+ ch = getc(fp);
+ else
+ state = st_host;
+ break;
+
+ /* --- Read a host name --- */
+
+ case st_host:
+ if (p == l)
+ die("string too long in `" file_SERVER "'");
+ if (ch != EOF && !isspace((unsigned char)ch) && ch != ':') {
+ *p++ = ch;
+ ch = getc(fp);
+ } else {
+ struct hostent *h;
+
+ *p++ = 0;
+ if ((h = gethostbyname(buff)) == 0)
+ die("unknown host `%s' in `" file_SERVER "'", buff);
+ memcpy(&t_host, h->h_addr, sizeof(t_host));
+ state = st_colon;
+ }
+ break;
+
+ /* --- Waiting for a colon coming up --- */
+
+ case st_colon:
+ if (ch == EOF)
+ state = st_commit;
+ else if (isspace((unsigned char)ch))
+ ch = getc(fp);
+ else if (ch == ':') {
+ state = st_preport;
+ ch = getc(fp);
+ }
+ else
+ state = st_commit;
+ break;
+
+ /* --- Clearing whitespace before a port number --- */
+
+ case st_preport:
+ if (ch == EOF)
+ state = st_commit;
+ else if (isspace((unsigned char)ch))
+ ch = getc(fp);
+ else {
+ state = st_port;
+ p = buff;
+ }
+ break;
+
+ /* --- Read a port number --- */
+
+ case st_port:
+ if (p == l)
+ die("string too long in `" file_SERVER "'");
+ if (ch != EOF && !isspace((unsigned char)ch) && ch != ':') {
+ *p++ = ch;
+ ch = getc(fp);
+ } else {
+ struct servent *s;
+
+ *p++ = 0;
+ s = getservbyname(buff, "udp");
+ if (!s && isdigit((unsigned char)buff[0]))
+ t_port = htons(atoi(buff));
+ else if (!s)
+ die("unknown service `%s' in `" file_SERVER "'", buff);
+ else
+ t_port = s->s_port;
+ state = st_commit;
+ }
+ break;
+
+ /* --- A server has been read successfully --- */
+
+ case st_commit:
+ if (n_serv == max_serv) {
+ max_serv *= 2;
+ serv = xrealloc(serv, max_serv * sizeof(*serv));
+ }
+ serv[n_serv].sin_family = AF_INET;
+ serv[n_serv].sin_addr = t_host;
+ serv[n_serv].sin_port = t_port;
+ n_serv++;
+ state = st_start;
+ p = buff;
+ t_port = port;
+ break;
+
+ /* --- A safety net for a broken parser --- */
+
+ default:
+ die("internal error: can't get here in check__client");
+ break;
+ }
+ }
+ }
+
+ fclose(fp);
+
+ /* --- Now start sending requests --- */
+
+ if (!n_serv)
+ die("no servers specified in `" file_SERVER "'");
+
+ IF_TRACING(TRACE_CLIENT, {
+ size_t i;
+
+ for (i = 0; i < n_serv; i++) {
+ trace(TRACE_CLIENT, "client: server %s port %i",
+ inet_ntoa(serv[i].sin_addr), ntohs(serv[i].sin_port));
+ }
+ })
+ return (check__ask(rq, serv, n_serv));
+}
+