msg_parser.c
512dcd98
 /*
  * $Id$
  *
  * sip msg. header proxy parser 
  *
  */
 
 #include "msg_parser.h"
 #include "string.h"
 
 #include "parser_f.h"
 #include "dprint.h"
 
 
 
 /* parses the first line, returns pointer to  next line  & fills fl;
    also  modifies buffer (to avoid extra copy ops) */
 char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 {
 	
 	char *tmp;
 	char* second;
 	char* third;
 	char* nl;
 	int offset;
 	
 	/* grammar:
 		request  =  method SP uri SP version CRLF
 		response =  version SP status  SP reason  CRLF
 		(version = "SIP/2.0")
 	*/
 	
 
 	/* see if it's a reply (status) */
 	tmp=eat_token(buffer, len);
 	if (tmp==buffer){
 		DPrint("ERROR: empty  or bad first line\n");
 		goto error1;
 	}
 	if ((strlen(SIP_VERSION)==(tmp-buffer)) &&
 		(memcmp(buffer,SIP_VERSION,tmp-buffer)==0)){
 		
 		fl->type=SIP_REPLY;
 	}else{
 		fl->type=SIP_REQUEST;
 	}
 	
 	offset=tmp-buffer;
 	second=eat_space(tmp, len-offset);
 	offset+=second-tmp;
 	if (second==tmp){
 		goto error;
 	}
 	*tmp=0; /* mark the end of the token */
 	fl->u.request.method=buffer;
 	
 	/* next element */
 	tmp=eat_token(second, len-offset);
 	offset+=tmp-second;
 	third=eat_space(tmp, len-offset);
 	offset+=third-tmp;
 	if(third==tmp){
 		goto error;
 	}
 	*tmp=0; /* mark the end of the token */
 	fl->u.request.uri=second;
 	/*  last part */
 	tmp=eat_token(third,len-offset);
 	offset+=tmp-third;
 	if (tmp==third){
 		goto error;
 	}
 	if (! is_empty(tmp, len-offset)){		
 		goto error;
 	}
 	nl=eat_line(tmp,len-offset);
 	*tmp=0;
 	fl->u.request.version=third;
 	
 	return nl;
 
 error:
 	DPrint("ERROR: bad %s first line\n", 
 		(fl->type==SIP_REPLY)?"reply(status)":"request");
 error1:
 	fl->type=SIP_INVALID;
 	DPrint("ERROR: at line 0 char %d\n", offset);
 	/* skip  line */
 	nl=eat_line(buffer,len);
 	return nl;
 }
 
 
 /* returns integer field name type */
 int field_name(char *s)
 {
 	if ((strcmp(s, "Via")==0 )||(strcmp(s,"v")==0))
 		return  HDR_VIA;
 	if ((strcmp(s, "To")==0)||(strcmp(s,"t")==0))
 		return HDR_TO;
 	return HDR_OTHER;
 }
 
 
 
 
 /* returns pointer to next header line, and fill hdr_f */
 char* get_hdr_field(char *buffer, unsigned int len, struct hdr_field*  hdr_f)
 {
 	/* grammar (rfc822):
 		field = field-name ":" field-body CRLF
 		field-body = text [ CRLF SP field-body ]
 	   (CRLF in the field body must be removed)
 	*/
 
 	char* tmp;
 	char* nl;
 	char* body;
 	int offset;
 
 	if ((*buffer=='\n')||(*buffer=='\r')){
 		/* double crlf */
 		tmp=eat_line(buffer,len);
 		hdr_f->type=HDR_EOH;
 		return tmp;
 	}
 	
 	tmp=eat_token2(buffer, len, ':');
 	if ((tmp==buffer) || (tmp-buffer==len) ||
 		(is_empty(buffer, tmp-buffer-1))|| (*tmp!=':')){
 		hdr_f->type=HDR_ERROR;
 		goto error;
 	}
 	*tmp=0;
 	hdr_f->type=field_name(buffer);
 	body= ++tmp;
 	hdr_f->name=buffer;
 	offset=tmp-buffer;
 	/* get all the lines in this field  body */
 	do{
 		nl=eat_line(tmp, len-offset);
 		offset+=nl-tmp;
 		tmp=nl;
 	
 	}while( (*tmp==' ' ||  *tmp=='\t') && (offset<len) );
 	if (offset==len){
 		hdr_f->type=HDR_ERROR;
 		DPrint("ERROR: field body too  long\n");
 		goto error;
 	}
 	*(tmp-1)=0; /* should be an LF */
 	hdr_f->body=body;
 error:
 	return tmp;
 }
 
 
 
 char* parse_hostport(char* buf, char** host, short int* port)
 {
 	char *tmp;
 	char *invalid;
 	
 	*host=buf;
 	for(tmp=buf;(*tmp)&&(*tmp!=':');tmp++);
 	if (*tmp==0){
 		*port=0;
 	}else{
 		*tmp=0;
 		invalid=0;
 		*port=strtol(tmp+1, &invalid, 10);
 		if ((invalid!=0)&&(*invalid)){
 			DPrint("ERROR: hostport: trailing chars in port number: %s(%x)\n",
 					invalid, invalid);
 			/* report error? */
 		}
 	}
 	return *host;
 }
 
 
 
 /* parses a via body, returns next via (for compact vias) & fills vb,
  * the buffer should be null terminated! */
 char* parse_via_body(char* buffer,unsigned int len, struct via_body * vb)
 {
 	/* format: sent-proto sent-by  *(";" params) [comment] 
 
 	           sent-proto = name"/"version"/"transport
 		   sent-by    = host [":" port]
 		   
 	*/
 
 	char* tmp;
 	char *name,*version, *transport, *comment, *params, *hostport;
 	char * next_via;
 	char * host;
 	short int port;
 	int offset;
 	
 
 	name=version=transport=comment=params=hostport=next_via=host=0;
 	name=eat_space(buffer, len);
 	if (name-buffer==len) goto error;
 	offset=name-buffer;
 	tmp=name;
 
 	version=eat_token2(tmp,len-offset,'/');
 	if (version+1-buffer>=len) goto error;
 	*version=0;
 	version++;
 	offset+=version-tmp;
 	
 	transport=eat_token2(tmp,len-offset,'/');
 	if (transport+1-buffer>=len) goto error;
 	*transport=0;
 	transport++;
 	offset+=transport-tmp;
 	
 	tmp=eat_token(transport,len-offset);
 	if (tmp+1-buffer>=len) goto error;
 	*tmp=0;
 	tmp++;
 	offset+=tmp-transport;
 	
 	hostport=eat_space(tmp,len-offset);
 	if (hostport+1-buffer>=len) goto error;
 	offset+=hostport-tmp;
 
 	/* find end of hostport */
  	for(tmp=hostport; (tmp-buffer)<len &&
 			(*tmp!=' ')&&(*tmp!=';')&&(*tmp!=','); tmp++);
 	if (tmp-buffer<len){
 		switch (*tmp){
 			case ' ':
 				*tmp=0;
 				/*the rest is comment? */
 				if (tmp+1-buffer<len){
 					tmp++;
 					comment=tmp;
 					/* eat the comment */
 					for(;((tmp-buffer)<len)&&
 						(*tmp!=',');tmp++);
 					/* mark end of compact via (also end of comment)*/
 					if (tmp-buffer<len){
 						*tmp=0;
 					}else break;
 					/* eat space & ',' */
 					for(tmp=tmp+1;((tmp-buffer)<len)&&
 						(*tmp==' '|| *tmp==',');tmp++);
 					
 				}
 				break;
 
 			case ';':
 				*tmp=0;
 				if (tmp+1-buffer>=len) goto error;
 				tmp++;
 				params=tmp;
 				/* eat till end, first space  or ',' */
 				for(;((tmp-buffer)<len)&&
 					(*tmp!=' '&& *tmp!=',');tmp++);
 				if (tmp-buffer==len)  break;
 				if (*tmp==' '){
 					/* eat comment */
 					*tmp=0;
 					tmp++;
 					comment=tmp;
 					for(;((tmp-buffer)<len)&&
 						(*tmp!=',');tmp++);
 					if (tmp-buffer==len)  break;
 				}
 				/* mark end of via*/
 				*tmp=0;
 				/* eat space & ',' */
 				for(tmp=tmp+1;((tmp-buffer)<len)&&
 					(*tmp==' '|| *tmp==',');tmp++);
 				break;
 
 			case ',':
 				*tmp=0;
 				if (tmp+1-buffer<len){
 					/* eat space and ',' */
 					for(tmp=tmp+1; 
 						((tmp-buffer)<len)&&
 						(*tmp==' '|| *tmp==',');
 					   tmp++);
 				}
 		}
 	}
 	/* if we are not at the end of the body => we found another compact via */
 	if (tmp-buffer<len) next_via=tmp;
 	
 	/* parse hostport */
 	parse_hostport(hostport, &host, &port);
 	vb->name=name;
 	vb->version=version;
 	vb->transport=transport;
 	vb->host=host;
 	vb->port=port;
 	vb->params=params;
 	vb->comment=comment;
 	vb->next=next_via;
 	vb->error=VIA_PARSE_OK;
 
 	
 	/* tmp points to end of body or to next via (if compact)*/
 	
 	return tmp;
 
 error:
 	vb->error=VIA_PARSE_ERROR;
 	return tmp;
 }