utils/pdbt/pdb_server.c
d32fa103
 /*
  * Copyright (C) 2009 1&1 Internet AG
  *
  * This file is part of sip-router, a free SIP server.
  *
  * sip-router is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * sip-router is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
d32fa103
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include "pdb_server_backend.h"
 #include "log.h"
 
 
 
 #define NETBUFSIZE 200
 #define DEFAULT_BINDADDR "0.0.0.0"
 #define DEFAULT_PORT 5574
 
 
 
 
 void print_usage(char *program) {
 	set_log_level(LOG_INFO);
04fffc77
 	LINFO("version: pdb_server %d\n", PDB_VERSION);
 	LINFO("Listens on a UDP port for queries and sends answer UDP packets back.\n");
d32fa103
 	LINFO("\n");
 	LINFO("Usage: %s [<option>...]\n", program);
 	LINFO("  %s -m <data file> [-i <bind addr>] [-p <port>] [-d <log level>]\n", program);
 	LINFO("\n");
 	LINFO("  Options:\n");
 	LINFO("    -m <file>: Specifies the file containing the backend data.\n");
 	LINFO("    -i <bind addr>: Specifies the address to bind the UDP socket to.\n");
 	LINFO("                    Default is '%s'.\n", DEFAULT_BINDADDR);
 	LINFO("    -p <port>: Specifies the port to listen at in udp_server mode.\n");
 	LINFO("               Default is %ld.\n", (long int)DEFAULT_PORT);
 	LINFO("    -d <debug level>: %ld for debug level.\n", LOG_DEBUG);
 	LINFO("                      %ld for info level.\n", LOG_INFO);
 	LINFO("                      %ld for notice level.\n", LOG_NOTICE);
 	LINFO("                      %ld for warning level.\n", LOG_WARNING);
 	LINFO("                      %ld for error level.\n", LOG_ERR);
 	LINFO("                      %ld for critical level.\n", LOG_CRIT);
 	LINFO("                      %ld for alert level.\n", LOG_ALERT);
 	LINFO("                      %ld for emergency level.\n", LOG_EMERG);
 	LINFO("                      %ld to disable all messages.\n", LOG_EMERG-1);
 	LINFO("                      Default is warning level.\n");
04fffc77
 	LINFO("    -v: Print the version\n");
d32fa103
 	LINFO("    -h: Print this help.\n");
 }
 
f995edfb
 int pdb_msg_server_send(int so, char *buf, size_t answerlen, struct sockaddr *fromaddr, socklen_t fromaddrlen)
 {
 	ssize_t bytes_sent;
 	int try = 0;
 	again:
 		bytes_sent = sendto(so, buf, answerlen, 0, fromaddr, fromaddrlen);
 		if (bytes_sent < 3) {
 			if ((errno == EINTR) && (try < 3)) {
 				try++;
 				LERR("sendto() failed - trying again. errno=%d (%s)\n", errno, strerror(errno));
 				goto again;
 			}
 			LERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
 			if ((errno==EAGAIN)||(errno==EINTR)||(errno==EWOULDBLOCK)) return 0;
 			return -1;
 		}
 		if (bytes_sent != answerlen) {
 			LERR("cannot send the whole answer (%ld/%ld).\n", (long int)bytes_sent, (long int)answerlen);
 			return 0;
 		}
d32fa103
 
f995edfb
     return 0;
 }
d32fa103
 
 
 /*
  Receive query request and send answer via UDP.
  UDP Payload of request must contain the phone number (only digits allowed).
  The Answer contains the number of the request followed by '\0' and the carrier id.
  Loops until a receive or send error occurs and returns -1.
  However, the following errors are ignored: EAGAIN, EINTR, EWOULDBLOCK.
 */
 int udp_server(int so)
 {
f995edfb
     struct pdb_msg msg;
d32fa103
 	struct sockaddr fromaddr;
 	socklen_t fromaddrlen;
f995edfb
 	size_t answerlen = 0;
d32fa103
 	ssize_t bytes_received;
 	carrier_t carrierid;
f995edfb
 	char buf[sizeof(struct pdb_msg)];
d32fa103
 	int i;
 
 	for (;;) {
 		fromaddrlen = sizeof(fromaddr);
f995edfb
 		bytes_received = recvfrom(so, buf, sizeof(struct pdb_msg), 0, &fromaddr, &fromaddrlen);
d32fa103
 		if (bytes_received<0) {
f995edfb
             LERR("recvfrom() failed with errno=%d (%s)\n", errno, strerror(errno));
d32fa103
 			if ((errno==EAGAIN)||(errno==EINTR)||(errno==EWOULDBLOCK)) continue;
 			return -1;
 		}
 
f995edfb
         switch (buf[0]) {
             case PDB_VERSION_1:
                 /* get received bytes */
                 memcpy(&msg, buf, bytes_received);
 //                pdb_msg_dbg(msg);
                 short int *_id = (short int *)&(msg.hdr.id); /* make gcc happy */
                 msg.hdr.id = ntohs(*_id);
 
                 i = 0;
                 while (i < strlen(msg.bdy.payload)) {
                     if (msg.bdy.payload[i] < '0' || msg.bdy.payload[i] > '9') {
                         pdb_msg_format_send(&msg, PDB_VERSION_1, PDB_TYPE_REPLY_ID, PDB_CODE_NOT_NUMBER, htons(msg.hdr.id), NULL, 0);
                         goto msg_send;
                     }
                     i++;
                 }
                 /* lookup pdb_id */
                 carrierid=lookup_number(msg.bdy.payload);
 
                 /* check if not found pdb_id */
                 if (carrierid == 0) {
                     pdb_msg_format_send(&msg, PDB_VERSION_1, PDB_TYPE_REPLY_ID, PDB_CODE_NOT_FOUND, htons(msg.hdr.id), NULL, 0);
                     goto msg_send;
                 }
 
                 /* convert to network byte order*/
                 carrierid = htons(carrierid);
 
                 /* prepare the message payload to be sent
                  * add the number string and append the carrier id
                  */
                 memcpy(buf, msg.bdy.payload, msg.hdr.length - sizeof(msg.hdr));
                 memcpy(buf + msg.hdr.length - sizeof(msg.hdr), &carrierid, sizeof(carrierid));
 
                 /* all ok, send pdb_msg with pdb_id in payload */
                 pdb_msg_format_send(&msg, PDB_VERSION_1, PDB_TYPE_REPLY_ID, PDB_CODE_OK, htons(msg.hdr.id), buf, msg.hdr.length - sizeof(msg.hdr) + sizeof(carrierid));
                 goto msg_send;
 
                 break;
 
             /* old pdb version; no pdb_msg used */
             default:
                 /* take only digits */
                 i=0;
                 while ((i<bytes_received) && (buf[i]>='0') && (buf[i]<='9')) i++;
                 buf[i]=0; /* terminate string */
                 i++;
 
                 /* lookup pdb_id */
                 carrierid=lookup_number(buf);
 
                 /* convert to network byte order*/
                 carrierid=htons(carrierid);
 
                 /* append carrier id to answer */
                 memcpy(&(buf[i]), &carrierid, sizeof(carrierid));
                 answerlen=i+sizeof(carrierid);
                 goto buf_send;
 
                 break;
         }
 
 msg_send:
 //        pdb_msg_dbg(msg);
         if (pdb_msg_server_send(so, (char*)&msg, msg.hdr.length, &fromaddr, fromaddrlen) < 0) {
             return -1;
         }
         continue;
 
 buf_send:
         if (pdb_msg_server_send(so, buf, answerlen, &fromaddr, fromaddrlen)) {
             return -1;
         }
         continue;
     }
 
 	return -1;
d32fa103
 }
 
 
 
 
 int main(int argc, char *argv[]) {
 	int opt;
 	char *backend_data_filename = NULL;
 	char *bind_addr = DEFAULT_BINDADDR;
 	unsigned short bind_port = DEFAULT_PORT;
 	int use_syslog = 0;
 	int log_level=LOG_WARNING;
 
 	long int ret;
 
 	int so;
 	struct sockaddr_in sa;
 		
04fffc77
 	while ((opt = getopt(argc, argv, "m:i:p:vhdl:")) != -1) {
d32fa103
 		switch (opt) {
 		case 'm':
 			backend_data_filename = optarg;
 			break;
 		case 'i':
 			bind_addr=optarg;
 			break;
 		case 'p':
 			ret=strtol(optarg, NULL, 10);
 			if ((ret<0) || (ret>65535)) {
 				init_log("pdb_server", use_syslog);
 				LERR("invalid port '%s' specified.\n", optarg);
 				return -1;
 			}
 			bind_port=ret;
 			break;
04fffc77
 		case 'v':
 			set_log_level(LOG_INFO);
 			LINFO("version: pdb_server %d\n", PDB_VERSION);
 			return 0;
 			break;
d32fa103
 		case 'h':
 			init_log("pdb_server", use_syslog);
 			print_usage(argv[0]);
 			return 0;
 			break;
 		case 'd':
 			use_syslog=1;
 			break;
 		case 'l':
 			ret=strtol(optarg, NULL, 10);
 			if ((ret<LOG_EMERG-1) || (ret>LOG_DEBUG)) {
 				init_log("pdb_server", use_syslog);
 				LERR("invalid log level '%s' specified.\n", optarg);
 				return -1;
 			}
 			log_level=ret;
 			break;
 		default:
 			init_log("pdb_server", use_syslog);
 			LERR("invalid option '%c'.\n", opt);
 			print_usage(argv[0]);
 			return 1;
 		}
 	}
 
 	init_log("pdb_server", use_syslog);
 	set_log_level(log_level);
 
 	if (backend_data_filename==NULL) {
 		LERR("no data file specified.\n");
 		return 1;
 	}
 
 	if (init_backend(backend_data_filename)<0) {
 		LERR("cannot initialize backend.\n");
 		return -1;
 	}
 	
 	so = socket(AF_INET, SOCK_DGRAM, 0);
 	if (so<0) {
 		LERR("socket() failed with errno=%d (%s)\n", errno, strerror(errno));
 		return -1;
 	}
 	
 	memset(&sa, 0, sizeof(sa));
 	sa.sin_family = AF_INET;
 	sa.sin_port = htons(bind_port);
 	if (inet_aton(bind_addr, &(sa.sin_addr))==0) {
 		LERR("invalid address '%s'.\n", bind_addr);
 		close(so);
 		return -1;
 	}
 	
 	if (bind(so, (struct sockaddr *) &sa, sizeof(sa))<0) {
 		LERR("bind() failed with errno=%d (%s)\n", errno, strerror(errno));
 		close(so);
 		return -1;
 	}
 	
 	udp_server(so);
 	close(so);
 
 	return 0;
 }