Selaa lähdekoodia

port numbers different now for remote and local

Jiri Kuthan 24 vuotta sitten
vanhempi
commit
f571aa35b7
13 muutettua tiedostoa jossa 388 lisäystä ja 175 poistoa
  1. 13 2
      Makefile
  2. 2 0
      cfg.lex
  3. 3 0
      cfg.y
  4. 12 10
      forward.c
  5. 1 0
      globals.h
  6. 55 24
      main.c
  7. 91 34
      msg_parser.c
  8. 17 1
      msg_parser.h
  9. 7 14
      parser_f.c
  10. 23 11
      parser_f.h
  11. 13 5
      receive.c
  12. 107 9
      stats.h
  13. 44 65
      test/shoot.c

+ 13 - 2
Makefile

@@ -32,6 +32,8 @@ DEFS=-DNOCR -DMACROEATER -DSTATS -DOLD_PARSER -DDNS_IP_HACK #-DNO_DEBUG
 #-DNO_LOG
 
 PROFILE=  # -pg #set this if you want profiling
+#mode=release
+mode=debug
 
 # platform dependent settings
 
@@ -40,8 +42,15 @@ ARCH = $(shell uname -s)
 #common
 CC=gcc
 LD=gcc
-CFLAGS=-O2 -Wcast-align $(PROFILE) -Winline#-Wmissing-prototypes 
-LDFLAGS=-Wl,-O2 -Wl,-E $(PROFILE)
+
+ifeq ( mode, release )
+	CFLAGS=-O2 -Wcast-align $(PROFILE) -Winline#-Wmissing-prototypes 
+	LDFLAGS=-Wl,-O2 -Wl,-E $(PROFILE)
+else
+	CFLAGS=-g
+	LDFLAGS=-g
+endif
+
 LEX=flex
 YACC=bison
 YACC_FLAGS=-d -b cfg
@@ -127,3 +136,5 @@ proper: clean
 
 include $(depends)
 
+dbg: ser
+	gdb -command debug.gdb

+ 2 - 0
cfg.lex

@@ -81,6 +81,7 @@ LISTEN		listen
 DNS		 dns
 REV_DNS	 rev_dns
 PORT	port
+STAT	statistics
 MAXBUFFER maxbuffer
 CHILDREN children
 CHECK_VIA	check_via
@@ -154,6 +155,7 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{DNS}	{ count(); yylval.strval=yytext; return DNS; }
 <INITIAL>{REV_DNS}	{ count(); yylval.strval=yytext; return REV_DNS; }
 <INITIAL>{PORT}	{ count(); yylval.strval=yytext; return PORT; }
+<INITIAL>{STAT}	{ count(); yylval.strval=yytext; return STAT; }
 <INITIAL>{MAXBUFFER}	{ count(); yylval.strval=yytext; return MAXBUFFER; }
 <INITIAL>{CHILDREN}	{ count(); yylval.strval=yytext; return CHILDREN; }
 <INITIAL>{CHECK_VIA}	{ count(); yylval.strval=yytext; return CHECK_VIA; }

+ 3 - 0
cfg.y

@@ -19,6 +19,7 @@
 #include "dprint.h"
 #include "sr_module.h"
 
+
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
 #endif
@@ -73,6 +74,7 @@ void* f_tmp;
 %token DNS
 %token REV_DNS
 %token PORT
+%token STAT
 %token CHILDREN
 %token CHECK_VIA
 %token LOADMODULE
@@ -147,6 +149,7 @@ assign_stm:	DEBUG EQUAL NUMBER { debug=$3; }
 		| REV_DNS EQUAL NUMBER { received_dns|= ($3)?DO_REV_DNS:0; }
 		| REV_DNS EQUAL error { yyerror("boolean value expected"); }
 		| PORT EQUAL NUMBER   { port_no=$3; }
+		| STAT EQUAL STRING { stat_file=$3; }
 		| MAXBUFFER EQUAL NUMBER { maxbuffer=$3; }
 		| MAXBUFFER EQUAL error { yyerror("number expected"); }
 		| PORT EQUAL error    { yyerror("number expected"); } 

+ 12 - 10
forward.c

@@ -431,20 +431,20 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 
 	p->tx++;
 	p->tx_bytes+=new_len;
-#ifdef STATS
-	stats.total_tx++;
-#endif
 
 	if (udp_send(new_buf, new_len, (struct sockaddr*) to,
 				sizeof(struct sockaddr_in))==-1){
 			p->errors++;
 			p->ok=0;
+#ifdef STATS
+			update_fail_on_send;
+#endif
 			goto error;
 	} 
 #ifdef STATS
-	else stats.ok_tx_rq++;
+	/* sent requests stats */
+	else update_sent_request( msg->first_line.u.request.method_value );
 #endif
-
 	free(new_buf);
 	free(to);
 	/* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
@@ -552,17 +552,19 @@ int forward_reply(struct sip_msg* msg)
 		to->sin_port = (msg->via2.port)?htons(msg->via2.port):htons(SIP_PORT);
 		to->sin_addr.s_addr=*((long*)he->h_addr_list[0]);
 
-#ifdef STATS
-		stats.total_tx++;
-#endif
 #ifdef DNS_IP_HACK
 	}
 #endif
 	if (udp_send(new_buf,new_len, (struct sockaddr*) to, 
-					sizeof(struct sockaddr_in))==-1)
+					sizeof(struct sockaddr_in))==-1) 
+	{
+#ifdef STATS
+		update_fail_on_send;
+#endif
 		goto error;
+	}
 #ifdef STATS
-	else stats.ok_tx_rs++;
+	else update_sent_response(  msg->first_line.u.reply.statusclass );
 #endif
 	
 	free(new_buf);

+ 1 - 0
globals.h

@@ -15,6 +15,7 @@
 
 
 extern char * cfg_file;
+extern char *stat_file;
 extern unsigned short port_no;
 extern char port_no_str[];
 extern int port_no_str_len;

+ 55 - 24
main.c

@@ -13,6 +13,10 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+#include <sys/time.h>
 
 #include "config.h"
 #include "dprint.h"
@@ -26,8 +30,6 @@
 #include "stats.h"
 #endif
 
-
-
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
 #endif
@@ -94,8 +96,11 @@ Options:\n\
     -V           Version number\n\
     -h           This help message\n\
     -b nr        Maximum receive buffer size which will not be exceeded by\n\
-                 auto-probing procedure even if  OS allows\n\
-";
+                 auto-probing procedure even if  OS allows\n"
+#ifdef STATS
+"    -s file	 File to which statistics is dumped (disabled otherwise)\n"
+#endif
+;
 
 /* print compile-time constants */
 void print_ct_constants()
@@ -127,9 +132,9 @@ char* cfg_file = 0;
 unsigned short port_no = 0; /* port on which we listen */
 char port_no_str[MAX_PORT_LEN];
 int port_no_str_len=0;
-unsigned int maxbuffer = 128*1024; /* maximum buffer size we do not want to exceed
-				      durig the auto-probing procedure; may be
-				      re-configured */
+unsigned int maxbuffer = MAX_RECV_BUFFER_SIZE; /* maximum buffer size we do not want to exceed
+				      		durig the auto-probing procedure; may be
+				      		re-configured */
 int children_no = 0;           /* number of children processing requests */
 int debug = 0;
 int dont_fork = 0;
@@ -151,11 +156,6 @@ int process_no = 0;
 /* cfg parsing */
 int cfg_errors=0;
 
-#ifdef STATS
-/* jku: RX/TX statistics -- remember, they are process specific */
-struct stats_s stats;
-#endif
-
 
 #define MAX_FD 32 /* maximum number of inherited open file descriptors,
 		    (normally it shouldn't  be bigger  than 3) */
@@ -226,6 +226,9 @@ int main_loop()
 
 
 	if (dont_fork){
+#ifdef STATS
+		setstats( 0 );
+#endif
 		/* only one address */
 		if (udp_init(addresses[0],port_no)==-1) goto error;
 		/* receive loop */
@@ -241,6 +244,9 @@ int main_loop()
 				}
 				if (pid==0){
 					/* child */
+#ifdef STATS
+					setstats( i );
+#endif
 					return udp_rcv_loop();
 				}
 			}
@@ -260,6 +266,7 @@ int main_loop()
 
 }
 
+
 /* added by jku; allows for regular exit on a specific signal;
    good for profiling which only works if exited regularly and
    not by default signal handlers
@@ -267,17 +274,25 @@ int main_loop()
 
 static void sig_usr(int signo)
 {
-	DPrint("INT received, program terminates\n");
+	if (signo==SIGINT) {	/* exit gracefuly */
 #ifdef STATS
-	DPrint("ok_rx_rq\t%d\nok_rx_rs\t%d\nok_tx_rq\t%d\nok_tx_rs\t%d\ntotal_rx\t%d\ntotal_tx\t%d\n\n",
-		stats.ok_rx_rq, stats.ok_rx_rs, stats.ok_tx_rq, stats.ok_tx_rs, stats.total_rx, stats.total_tx );
-	DPrint("Thank you for flying ser\n");
+		/* print statistics on exit only for the first process */
+
+		if (stats->process_index==0 && stat_file )
+			if (dump_all_statistic()==0)
+				printf("statistic dumped to %s\n", stat_file );
+			else
+				printf("statistics dump to %s failed\n", stat_file );
 #endif
-	exit(0);
+		DPrint("INT received, program terminates\n");
+		DPrint("Thank you for flying ser\n");
+		exit(0);
+	} else if (signo==SIGUSR1) { /* statistic */
+		dump_all_statistic();
+	}
 }
 	
 	
-	
 int main(int argc, char** argv)
 {
 
@@ -286,20 +301,36 @@ int main(int argc, char** argv)
 	int c,r;
 	char *tmp;
 	struct utsname myname;
+	char *options;
 
 	/* added by jku: add exit handler */
         if (signal(SIGINT, sig_usr) == SIG_ERR ) {
- 		DPrint("ERROR: no signal handler can be installed\n");
+ 		DPrint("ERROR: no SIGINT signal handler can be installed\n");
+                goto error;
+        }
+#ifdef STATS
+	if (signal(SIGUSR1, sig_usr)  == SIG_ERR ) {
+                DPrint("ERROR: no SIGUSR1 signal handler can be installed\n");
                 goto error;
         }
+#endif
 
 	/* process command line (get port no, cfg. file path etc) */
 	opterr=0;
-	while((c=getopt(argc,argv,"f:p:b:l:n:rRvdDEVh"))!=-1){
+	options=
+#ifdef STATS
+	"s:"
+#endif
+	"f:p:b:l:n:rRvdDEVh";
+	
+	while((c=getopt(argc,argv,options))!=-1){
 		switch(c){
 			case 'f':
 					cfg_file=optarg;
 					break;
+			case 's':
+					stat_file=optarg;
+					break;
 			case 'p':
 					port_no=strtol(optarg, &tmp, 10);
 					if (tmp &&(*tmp)){
@@ -448,10 +479,6 @@ int main(int argc, char** argv)
 		names_len[r]=strlen(names[r]);
 	}
 
-#ifdef STATS
-	/* jku: initialize statistic */
- 	memset(&stats,0,sizeof(struct stats_s));
-#endif
 	
 	/* get ips */
 	printf("Listening on ");
@@ -467,6 +494,10 @@ int main(int argc, char** argv)
 				(unsigned short)port_no);
 	}
 
+#ifdef STATS
+	if (init_stats(  dont_fork ? 1 : children_no  )==-1) goto error;
+#endif
+
 	/* init_daemon? */
 	if (!dont_fork){
 		if ( daemonize(argv[0]) <0 ) goto error;

+ 91 - 34
msg_parser.c

@@ -34,8 +34,9 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 	char* third;
 	char* nl;
 	int offset;
-	int l;
+	/* int l; */
 	char* end;
+	char s1,s2,s3;
 	
 	/* grammar:
 		request  =  method SP uri SP version CRLF
@@ -46,37 +47,72 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 
 	end=buffer+len;
 	/* see if it's a reply (status) */
-	tmp=eat_token(buffer, len);
-	if ((tmp==buffer)||(tmp>=end)){
-		LOG(L_INFO, "ERROR:parse_first_line: empty  or bad first line\n");
+
+	/* jku  -- parse well-known methods */
+
+	/* drop messages which are so short they are for sure useless;
+           utilize knowledge of minimum size in parsing the first
+	   token 
+        */
+	if (len <=16 ) {
+		LOG(L_INFO, "ERROR: parse_first_line: message too short\n");
 		goto error1;
 	}
-	l=tmp-buffer;
-	if ((SIP_VERSION_LEN==l) &&
-		(memcmp(buffer,SIP_VERSION,l)==0)){
-		
-		fl->type=SIP_REPLY;
-	}else{
+
+	tmp=buffer;
+  	/* is it perhaps a reply, ie does it start with "SIP...." ? */
+	if ( 	(*tmp=='S' || *tmp=='s') && 
+		strncasecmp( tmp+1, SIP_VERSION+1, SIP_VERSION_LEN-1)==0 &&
+		(*(tmp+SIP_VERSION_LEN)==' ')) {
+			fl->type=SIP_REPLY;
+			fl->u.reply.version.len=SIP_VERSION_LEN;
+			tmp=buffer+SIP_VERSION_LEN;
+	} else IFISMETHOD( INVITE, 'I' )
+	else IFISMETHOD( CANCEL, 'C')
+	else IFISMETHOD( ACK, 'A' )
+	else IFISMETHOD( BYE, 'B' )
+	/* if you want to add another method XXX, include METHOD_XXX in
+           H-file (this is the value which you will take later in
+           processing and define XXX_LEN as length of method name;
+	   then just call IFISMETHOD( XXX, 'X' ) ... 'X' is the first
+	   latter; everything must be capitals
+	*/
+	else {
+		/* neither reply, nor any of known method requests, 
+		   let's believe it is an unknown method request
+        	*/
+		tmp=eat_token_end(buffer,buffer+len);
+		if ((tmp==buffer)||(tmp>=end)){
+			LOG(L_INFO, "ERROR:parse_first_line: empty  or bad first line\n");
+			goto error1;
+		}
+		if (*tmp!=' ') {
+			LOG(L_INFO, "ERROR:parse_first_line: method not followed by SP\n");
+			goto error1;
+		}
 		fl->type=SIP_REQUEST;
+		fl->u.request.method_value=METHOD_OTHER;
+		fl->u.request.method.len=tmp-buffer;
 	}
-	
-	offset=l;
-	second=eat_space(tmp, len-offset);
-	offset+=second-tmp;
-	if ((second==tmp)||(tmp>=end)){
-		goto error;
-	}
-	*tmp=0; /* mark the end of the token */
-	fl->u.request.method.s=buffer;
-	fl->u.request.method.len=l;
+
+
+	/* identifying type of message over now; 
+	   tmp points at space after; go ahead */
+
+	fl->u.request.method.s=buffer;  /* store ptr to first token */
+	(*tmp)=0;			/* mark the 1st token end */
+	second=tmp+1;			/* jump to second token */
+	offset=second-buffer;
+
+/* EoJku */
 	
 	/* next element */
-	tmp=eat_token(second, len-offset);
+	tmp=eat_token_end(second, second+len-offset);
 	if (tmp>=end){
 		goto error;
 	}
 	offset+=tmp-second;
-	third=eat_space(tmp, len-offset);
+	third=eat_space_end(tmp, tmp+len-offset);
 	offset+=third-tmp;
 	if ((third==tmp)||(tmp>=end)){
 		goto error;
@@ -85,20 +121,41 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 	fl->u.request.uri.s=second;
 	fl->u.request.uri.len=tmp-second;
 
+	/* jku: parse status code */
+	if (fl->type==SIP_REPLY) {
+		if (fl->u.request.uri.len!=3) {
+			LOG(L_INFO, "ERROR:parse_first_line: len(status code)!=3: %s\n",
+				second );
+			goto error;
+		}
+		s1=*second; s2=*(second+1);s3=*(second+2);
+		if (s1>='0' && s1<='9' && 
+		    s2>='0' && s2<='9' &&
+		    s3>='0' && s3<='9' ) {
+			fl->u.reply.statusclass=s1-'0';
+			fl->u.reply.statuscode=fl->u.reply.statusclass*100+10*(s2-'0')+(s3-'0');
+		} else {
+			LOG(L_INFO, "ERROR:parse_first_line: status_code non-numerical: %s\n",
+				second );
+			goto error;
+		}
+	}
+	/* EoJku */
+
 	/*  last part: for a request it must be the version, for a reply
 	 *  it can contain almost anything, including spaces, so we don't care
 	 *  about it*/
 	if (fl->type==SIP_REQUEST){
-		tmp=eat_token(third,len-offset);
+		tmp=eat_token_end(third,third+len-offset);
 		offset+=tmp-third;
 		if ((tmp==third)||(tmp>=end)){
 			goto error;
 		}
-		if (! is_empty(tmp, len-offset)){
+		if (! is_empty_end(tmp, tmp+len-offset)){
 			goto error;
 		}
 	}else{
-		tmp=eat_token2(third,len-offset,'\r'); /* find end of line 
+		tmp=eat_token2_end(third,third+len-offset,'\r'); /* find end of line 
 												  ('\n' or '\r') */
 		if (tmp>=end){ /* no crlf in packet => invalid */
 			goto error;
@@ -240,22 +297,22 @@ char* get_hdr_field(char *buffer, unsigned int len, struct hdr_field*  hdr_f)
 		return tmp;
 	}
 	
-	tmp=eat_token2(buffer, len, ':');
+	tmp=eat_token2_end(buffer, buffer+len, ':');
 	if ((tmp==buffer) || (tmp-buffer==len) ||
-		(is_empty(buffer, tmp-buffer))|| (*tmp!=':')){
+		(is_empty_end(buffer, tmp))|| (*tmp!=':')){
 		hdr_f->type=HDR_ERROR;
 		goto error;
 	}
 	*tmp=0;
 	/* take care of possible spaces (e.g: "Via  :") */
-	tmp2=eat_token(buffer, tmp-buffer);
+	tmp2=eat_token_end(buffer, tmp);
 	/* in the worst case tmp2=buffer+tmp-buffer=tmp */
 	*tmp2=0;
 	l=tmp2-buffer;
 	if (tmp2<tmp){
 		tmp2++;
 		/* catch things like: "Via foo bar:" */
-		tmp2=eat_space(tmp2, tmp-tmp2);
+		tmp2=eat_space_end(tmp2, tmp);
 		if (tmp2!=tmp){
 			hdr_f->type=HDR_ERROR;
 			goto error;
@@ -492,33 +549,33 @@ char* parse_via_body(char* buffer,unsigned int len, struct via_body * vb)
 
 	name=version=transport=comment=params=hostport=next_via=host.s=0;
 	name_len=version_len=transport_len=comment_len=params_len=host.len=0;
-	name=eat_space(buffer, len);
+	name=eat_space_end(buffer, buffer+len);
 	if (name-buffer==len) goto error;
 	offset=name-buffer;
 	tmp=name;
 
-	version=eat_token2(tmp,len-offset,'/');
+	version=eat_token2_end(tmp,tmp+len-offset,'/');
 	if (version+1-buffer>=len) goto error;
 	*version=0;
 	name_len=version-name;
 	version++;
 	offset+=version-tmp;
 	
-	transport=eat_token2(tmp,len-offset,'/');
+	transport=eat_token2_end(tmp,tmp+len-offset,'/');
 	if (transport+1-buffer>=len) goto error;
 	*transport=0;
 	version_len=transport-version;
 	transport++;
 	offset+=transport-tmp;
 	
-	tmp=eat_token(transport,len-offset);
+	tmp=eat_token_end(transport,transport+len-offset);
 	if (tmp+1-buffer>=len) goto error;
 	*tmp=0;
 	transport_len=tmp-transport;
 	tmp++;
 	offset+=tmp-transport;
 	
-	hostport=eat_space(tmp,len-offset);
+	hostport=eat_space_end(tmp,tmp+len-offset);
 	if (hostport+1-buffer>=len) goto error;
 	offset+=hostport-tmp;
 

+ 17 - 1
msg_parser.h

@@ -18,7 +18,21 @@ enum {	HDR_EOH=-1, HDR_ERROR=0, HDR_OTHER,
 		HDR_MAXFORWARDS, HDR_ROUTE
 	};
 
-
+#define INVITE_LEN	6
+#define ACK_LEN		3
+#define CANCEL_LEN	6
+#define BYE_LEN		3
+enum { METHOD_OTHER, METHOD_INVITE, METHOD_CANCEL, METHOD_ACK, METHOD_BYE };
+
+#define IFISMETHOD(methodname,firstchar)                                  \
+if (  (*tmp==(firstchar) || *tmp==((firstchar) | 32)) &&                  \
+        strncasecmp( tmp+1, #methodname +1, methodname##_LEN-1)==0 &&     \
+        *(tmp+methodname##_LEN)==' ') {                                   \
+                fl->type=SIP_REQUEST;                                     \
+                fl->u.request.method.len=methodname##_LEN;                \
+                fl->u.request.method_value=METHOD_##methodname;           \
+                tmp=buffer+methodname##_LEN;                              \
+}
 
 
 #define VIA_PARSE_OK	1
@@ -35,11 +49,13 @@ struct msg_start{
 			str method;
 			str uri;
 			str version;
+			short method_value;
 		}request;
 		struct {
 			str version;
 			str status;
 			str reason;
+			unsigned short statusclass, statuscode;
 		}reply;
 	}u;
 };

+ 7 - 14
parser_f.c

@@ -41,6 +41,9 @@ char* eat_line(char* buffer, unsigned int len)
 
 /* returns pointer to first non  white char or after the end  of the buffer */
 
+/* MACROEATER no more optional */
+
+/*
 #ifndef MACROEATER
 
 char* eat_space(char* buffer, unsigned int len)
@@ -52,7 +55,7 @@ char* eat_space(char* buffer, unsigned int len)
 }
 
 
-/* returns pointer after the token (first whitespace char or CR/LF) */
+// returns pointer after the token (first whitespace char or CR/LF) 
 char* eat_token(char* buffer, unsigned int len)
 {
 	char *p;
@@ -65,7 +68,7 @@ char* eat_token(char* buffer, unsigned int len)
 
 
 
-/* returns pointer after the token (first delim char or CR/LF) */
+// returns pointer after the token (first delim char or CR/LF)
 char* eat_token2(char* buffer, unsigned int len, char delim)
 {
 	char *p;
@@ -75,18 +78,8 @@ char* eat_token2(char* buffer, unsigned int len, char delim)
 		p++);
 	return p;
 }
-
-/* EoMACROEATER */
 #endif
+*/
 
+/* EoMACROEATER */
 
-
-/* returns true if line started  at buffer contains only white space */
-int is_empty(char* buffer, unsigned int len)
-{
-	char *p;
-	
-	p=eat_space(buffer, len);
-	if ((p < buffer+len ) && (*p=='\r' || *p=='\n')) return 1;
-	return 0;
-}

+ 23 - 11
parser_f.h

@@ -6,36 +6,47 @@
 #define parser_f_h
 
 char* eat_line(char* buffer, unsigned int len);
+
+/* macro now
 int is_empty(char* buffer, unsigned int len);
+*/
 
-#ifdef MACROEATER
+/* MACROEATER no more optional */
+/* #ifdef MACROEATER */
 
 /* turn the most frequently called functions into macros */
 
 
-#define eat_space(buffer,len)                                          \
-  ( {   char *p;                                                     	\
-        for(p=(buffer);(p<(buffer)+(len))&& (*p==' ' || *p=='\t') ;p++);\
+#define eat_space_end(buffer,pend)                                       \
+  ( {   char *p;                                                 	\
+        for(p=(buffer);(p<pend)&& (*p==' ' || *p=='\t') ;p++);		\
         p;                                                              \
   } )
 
-#define eat_token(buffer,len)						\
-  ( { char *p;								\
-      for (p=(buffer);(p<(buffer)+(len))&&				\
+#define eat_token_end(buffer,pend)					\
+  ( { char *p       ;							\
+      for (p=(buffer);(p<pend)&&					\
                         (*p!=' ')&&(*p!='\t')&&(*p!='\n')&&(*p!='\r');	\
                 p++);							\
       p;								\
   } )
 
-#define eat_token2(buffer,len,delim)					\
-  ( { char *p;								\
-      for (p=(buffer);(p<(buffer)+(len))&&				\
+#define eat_token2_end(buffer,pend,delim)					\
+  ( { char *p       ;							\
+      for (p=(buffer);(p<pend)&&					\
                         (*p!=(delim))&&(*p!='\n')&&(*p!='\r');		\
                 p++);							\
       p;								\
   } )
 
+#define is_empty_end(buffer, pend )					\
+  ( { char *p;								\
+      p=eat_space_end( buffer, pend );					\
+      ((p<pend ) && (*p=='\r' || *p=='\n')) ? 1 : 0;			\
+  } )
+
 
+/*
 #else
 
 
@@ -43,7 +54,8 @@ char* eat_space(char* buffer, unsigned int len);
 char* eat_token(char* buffer, unsigned int len);
 char* eat_token2(char* buffer, unsigned int len, char delim);
 
-/* EoMACROEATER */
 #endif
+*/
+/* EoMACROEATER */
 
 #endif

+ 13 - 5
receive.c

@@ -24,9 +24,8 @@
 int receive_msg(char* buf, unsigned int len, unsigned long src_ip)
 {
 	struct sip_msg msg;
-
 #ifdef STATS
-	stats.total_rx++;	
+	int skipped = 1;
 #endif
 
 	memset(&msg,0, sizeof(struct sip_msg)); /* init everything to 0 */
@@ -65,8 +64,8 @@ int receive_msg(char* buf, unsigned int len, unsigned long src_ip)
 			goto error;
 		}
 #ifdef STATS
-		/* jku -- update statistics  */
-		else stats.ok_rx_rq++;	
+		/* jku -- update request statistics  */
+		else update_received_request(  msg.first_line.u.request.method_value );
 #endif
 	}else if (msg.first_line.type==SIP_REPLY){
 		DBG("msg= reply\n");
@@ -83,7 +82,7 @@ int receive_msg(char* buf, unsigned int len, unsigned long src_ip)
 
 #ifdef STATS
 		/* jku -- update statistics  */
-		stats.ok_rx_rs++;	
+		update_received_response(  msg.first_line.u.reply.statusclass );
 #endif
 		
 		/* send the msg */
@@ -93,11 +92,17 @@ int receive_msg(char* buf, unsigned int len, unsigned long src_ip)
 						(unsigned short) msg.via2.port);
 		}
 	}
+#ifdef STATS
+	skipped = 0;
+#endif
 skip:
 	if (msg.new_uri.s) { free(msg.new_uri.s); msg.new_uri.len=0; }
 	if (msg.add_rm) free_lump_list(msg.add_rm);
 	if (msg.repl_add_rm) free_lump_list(msg.repl_add_rm);
 	free(msg.orig);
+#ifdef STATS
+	if (skipped) update_received_drops;
+#endif
 	return 0;
 error:
 	if (msg.new_uri.s) free(msg.new_uri.s);
@@ -105,6 +110,9 @@ error:
 	if (msg.repl_add_rm) free_lump_list(msg.repl_add_rm);
 	free(msg.orig);
 error1:
+#ifdef STATS
+	update_received_drops;
+#endif
 	return -1;
 }
 

+ 107 - 9
stats.h

@@ -1,22 +1,120 @@
 #ifndef stats_h
 #define stats_h
 
+#include <ctype.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+
 #ifdef STATS
 
+
 struct stats_s {
 
-	/* total/valid, received/sent, request/response */
-	unsigned long 	ok_rx_rq,
-			ok_rx_rs,
-			ok_tx_rq,
-			ok_tx_rs,
-			total_rx,
-			total_tx;
+	unsigned int	process_index;
+	pid_t		pid;
+	time_t		start_time;
+
+	unsigned long 
+
+	/* received packets */
+
+	received_requests_inv, 		/* received_requests */
+	received_requests_ack,
+	received_requests_cnc,
+	received_requests_bye,
+	received_requests_other,
+
+	received_responses_1, 		/* received_requests */
+	received_responses_2,
+	received_responses_3,
+	received_responses_4,
+	received_responses_5,
+	received_responses_6,
+	received_responses_other,
+
+	received_drops, 		/* all messages we received and did not process
+					   successfully; reasons include SIP sanity checks 
+					   (missing Vias, neither request nor response, 
+					   failed parsing), ser errors (malloc, action
+					   failure)
+					*/
+
+	/* sent */
+
+	/* sent_requests */
+	sent_requests_inv,
+	sent_requests_ack,
+	sent_requests_cnc,
+	sent_requests_bye,
+	sent_requests_other,
+
+	/* sent responses */
+	sent_responses_1,
+	sent_responses_2,
+	sent_responses_3,
+	sent_responses_4,
+	sent_responses_5,
+	sent_responses_6,
+
+	failed_on_send;			
+			  
 };
 
 
-extern struct stats_s stats;
+extern struct stats_s *stats;
 
-#endif
+void setstats( int child_index );
+void dump_statistic( FILE *fp, struct stats_s *istats );
+int dump_all_statistic();
+int init_stats( int nr_of_processes );
+
+#define _update_request( method, dir )			\
+	{ if (stat_file!=NULL) switch( method ) {	\
+          	case METHOD_INVITE: stats->dir##_requests_inv++; break;	\
+          	case METHOD_ACK: stats->dir##_requests_ack++; break;		\
+          	case METHOD_CANCEL: stats->dir##_requests_cnc++; break;	\
+          	case METHOD_BYE: stats->dir##_requests_bye++; break;		\
+          	case METHOD_OTHER: stats->dir##_requests_other++; break;	\
+          	default: LOG(L_ERR, "ERROR: unknown method in rq stats (%s)\n", #dir);	\
+		}	\
+        }
+
+#define update_received_request( method ) _update_request( method, received )
+#define update_sent_request( method ) _update_request( method, sent )
+
+#define         _statusline(class, dir )       case class: stats->dir##_responses_##class++; break;
+/*
+#define		statusline( class )	_statusline( class, received )
+#define		statusline2( class )	_statusline( class, sent )
+*/
 
+#define _update_response( statusclass, dir )		\
+        { if (stat_file!=NULL)                          \
+                switch( statusclass ) {                 \
+                        _statusline(1, dir)                   \
+                        _statusline(2, dir)                   \
+                        _statusline(3, dir)                   \
+                        _statusline(4, dir)                   \
+                        _statusline(5, dir)                   \
+                        _statusline(6, dir)                   \
+                        default: LOG(L_INFO, "ERROR: unusual status code received in stats (%s)\n", #dir);    \
+                }       \
+        }
+
+#define update_received_response( statusclass ) _update_response( statusclass, received )
+#define update_sent_response( statusclass ) _update_response( statusclass, sent )
+
+#define update_received_drops	{  stats->received_drops++; }
+#define update_fail_on_send	{  stats->failed_on_send++; }
+
+
+#endif
 #endif

+ 44 - 65
test/shoot.cpp → test/shoot.c

@@ -8,45 +8,23 @@ bouquets and brickbats to [email protected]
 /* changes by [email protected]; now messages can be really received;
    status code returned is 2 for some local errors , 0 for success
    and 1 for remote error -- ICMP/timeout; can be used to test if
-   a server is alive; 1xx messages are now ignored
+   a server is alive; 1xx messages are now ignored; windows support
+   dropped
 */
 
-/* currently, compiles only for Solaris; Linux returns
- /usr/include/regexp.h:131: cannot convert `char *' to `unsigned char *' 
-*/
-
-int 		regerr;
-
-#define INIT         register char *sp = instring;
-#define GETC()       (*sp++)
-#define PEEKC()      (*sp)
-#define UNGETC(c)    (--sp)
-/*#define RETURN(*c)    return; */
-#define RETURN(c)    return c;
-#define ERROR(c)     regerr
-#include <regexp.h>
-
-
+#include <stdlib.h>
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
 #include <string.h>
 #include <ctype.h>
 #include <time.h>
-#include <sys/types.h>
-#include <stdlib.h>
-/* windows specific headers */
-#ifdef WIN32
-#include <windows.h>
-#include <winsock.h>
-#define close(a) closesocket(a)
-#else
-/* *nix specific networking headers */
-#include <sys/time.h>
 #include <unistd.h>
 #include <netdb.h>
-#include <arpa/inet.h>
 #include <sys/socket.h>
-#include <netinet/in.h>
-#endif
+
+#include <regex.h>
+regex_t* regexp;
 
 #define RESIZE		1024
 
@@ -67,8 +45,8 @@ long getaddress(char *host)
 {
 	int i, dotcount=0;
 	char *p = host;
-	struct hostent		*pent;
-	/* struct sockaddr_in	addr; */ /* see the note on portabilit at the end of the routine */
+	struct hostent* pent;
+	long l, *lp;
 
 	/*try understanding if this is a valid ip address
 	we are skipping the values of the octets specified here.
@@ -106,11 +84,9 @@ long getaddress(char *host)
 		exit(2);
 	}
 
-	/* PORTABILITY-ISSUE: replacing a costly memcpy call with a hack, may not work on 
-	some systems.  
-	memcpy(&addr.sin_addr, (pent->h_addr), pent->h_length);
-	return addr.sin_addr.s_addr; */
-	return *((long *)(pent->h_addr));
+	lp = (long *) (pent->h_addr);
+	l = *lp;
+	return l;
 }
 
 
@@ -129,18 +105,20 @@ at 5 seconds (5000 milliseconds).
 we are detecting the final response without a '1' as the first
 letter.
 */
-void shoot(char *buff, long address, int port)
+void shoot(char *buff, long address, int lport, int rport )
 {
 	struct sockaddr_in	addr;
 	/* jku - b  server structures */
 	struct sockaddr_in	sockname;
 	int ssock;
+	/*
 	char compiledre[ RESIZE ];
+	*/
 	/* jku - e */
 	int retryAfter = 500, i, len, ret;
 	int	nretries = 10;
 	int	sock;
-	timeval	tv;
+	struct timeval	tv;
 	fd_set	fd;
 	char	reply[1600];
 
@@ -160,26 +138,28 @@ void shoot(char *buff, long address, int port)
 
 	sockname.sin_family=AF_INET;
 	sockname.sin_addr.s_addr = htonl( INADDR_ANY );
-	sockname.sin_port = htons((short)port);
-	if (bind( ssock, (sockaddr *) &sockname, sizeof(sockname) )==-1) {
+	sockname.sin_port = htons((short)lport);
+	if (bind( ssock, (struct sockaddr *) &sockname, sizeof(sockname) )==-1) {
 		perror("no bind");
 		exit(2);
 	}
 
 	/* should capture: SIP/2.0 100 Trying */
-	compile("^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", compiledre, &compiledre[RESIZE], '\0');
+	/* compile("^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", compiledre, &compiledre[RESIZE], '\0'); */
+	regexp=(regex_t*)malloc(sizeof(regex_t));
+	regcomp(regexp, "^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); 
 	
 
 	/* jku - e */
 
 	addr.sin_addr.s_addr = address;
-	addr.sin_port = htons((short)port);
+	addr.sin_port = htons((short)rport);
 	addr.sin_family = AF_INET;
 	
 	/* we connect as per the RFC 2543 recommendations
 	modified from sendto/recvfrom */
 
-	ret = connect(sock, (sockaddr *)&addr, sizeof(addr));
+	ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
 	if (ret==-1) {
 		perror("no connect");
 		exit(2);
@@ -241,7 +221,8 @@ void shoot(char *buff, long address, int port)
 			puts("/* reply */");
 			puts(reply);
 			putchar('\n');
-			if (step( reply, compiledre )) {
+			/* if (step( reply, compiledre )) { */
+			if (regexec((regex_t*)regexp, reply, 0, 0, 0)==0) {
 				puts(" provisional received; still waiting for a final response\n ");
 				continue;
 			} else {
@@ -266,21 +247,12 @@ int main(int argc, char *argv[])
 	FILE	*pf;
 	char	buff[1600];
 	int		length;
-	int		port;
-#ifdef WIN32
-	WSADATA	wsadata;
-	int err = WSAStartup(0x0101, &wsadata);
-	if (err != 0)
-	{
-		printf("shoot cannot be used as TCP/IP is not available.\n");
-		exit(0);
-	}
-#endif
+	int	lport=0;
+	int	rport=5060;
 
-
-	if (argc != 3 && argc != 4)
+	if (! (argc >= 3 && argc <= 5))
 	{
-		puts("usage: shoot file host [port]");
+		puts("usage: shoot file host [rport] [lport]");
 		exit(2);
 	}
 
@@ -292,14 +264,21 @@ int main(int argc, char *argv[])
 	}
 
 	/* take the port as 5060 even if it is incorrectly specified */
-	if (argc == 4)
+	if (argc >= 4)
 	{
-		port = atoi(argv[3]);
-		if (!port)
-			port = 5060;
+		rport = atoi(argv[3]);
+		if (!rport) {
+			puts("error: non-numerical remote port number");
+			exit(1);
+		}
+		if (argc==5) {
+			lport=atoi(argv[4]);
+			if (!lport) {
+				puts("error: non-numerical local port number");
+				exit(1);
+			}
+		}
 	}
-	else
-		port = 5060;
 
 	/* file is opened in binary mode so that the cr-lf is preserved */
 	pf = fopen(argv[1], "rb");
@@ -317,7 +296,7 @@ int main(int argc, char *argv[])
 	fclose(pf);
 	buff[length] = 0;
 
-	shoot(buff, address, port);
+	shoot(buff, address, lport, rport );
 
 	/* visual studio closes the debug console as soon as the 
 	program terminates. this is to hold the window from collapsing