/* * $Id$ * * sip first line parsing automaton * */ #include "dprint.h" #include "msg_parser.h" /* grammar: request = method SP uri SP version CRLF response = version SP status SP reason CRLF (version = "SIP/2.0") */ /*known methods: INVITE, ACK, CANCEL, BYE*/ enum{ START, INVITE1, INVITE2, INVITE3, INVITE4, INVITE5, ACK1, ACK2, CANCEL1, CANCEL2, CANCEL3, CANCEL4, CANCEL5, BYE1, BYE2, SIP1, SIP2, SIP3, SIP4, SIP5, SIP6, FIN_INVITE=100, FIN_ACK, FIN_CANCEL, FIN_BYE, FIN_SIP, P_METHOD=200, L_URI, P_URI, L_VER, VER1, VER2, VER3, VER4, VER5, VER6, FIN_VER, L_STATUS, P_STATUS, L_REASON, P_REASON, L_LF, F_CR, F_LF }; char* parse_fline(char* buffer, char* end, struct msg_start* fl) { char* tmp; register int state; unsigned short stat; stat=0; fl->type=SIP_REQUEST; state=START; for(tmp=buffer;tmp<end;tmp++){ switch(*tmp){ case ' ': case '\t': switch(state){ case START: /*allow space at the beginnig, althoug not legal*/ break; case L_URI: case L_VER: case L_STATUS: case L_REASON: case L_LF: /*eat space*/ break; case FIN_INVITE: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_INVITE; state=L_URI; break; case FIN_ACK: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_ACK; state=L_URI; break; case FIN_CANCEL: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_CANCEL; state=L_URI; break; case FIN_BYE: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_BYE; state=L_URI; break; case FIN_SIP: *tmp=0; fl->u.reply.version.len=tmp-fl->u.reply.version.s; state=L_STATUS; fl->type=SIP_REPLY; break; case P_URI: *tmp=0; fl->u.request.uri.len=tmp-fl->u.request.uri.s; state=L_VER; break; case FIN_VER: *tmp=0; fl->u.request.version.len=tmp-fl->u.request.version.s; state=L_LF; break; case P_STATUS: *tmp=0; fl->u.reply.status.len=tmp-fl->u.reply.status.s; state=L_REASON; break; case P_REASON: /* *tmp=0; fl->u.reply.reason.len=tmp-fl->u.reply.reason.s; */ break; case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; case P_METHOD: default: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_OTHER; state=L_URI; } break; case 's': case 'S': switch(state){ case START: state=SIP1; fl->u.reply.version.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: fl->u.request.version.s=tmp; state=VER1; break; case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'i': case 'I': switch(state){ case START: state=INVITE1; fl->u.request.method.s=tmp; break; case INVITE3: state=INVITE4; break; case SIP1: state=SIP2; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER1: state=VER2; break; case L_VER: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'p': case 'P': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP2: state=SIP3; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER2: state=VER3; break; case L_VER: case VER1: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case '/': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP3: state=SIP4; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER3: state=VER4; break; case L_VER: case VER1: case VER2: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case '2': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP4: state=SIP5; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: stat=stat*10+*tmp-'0'; break; case L_STATUS: stat=*tmp-'0'; fl->u.reply.status.s=tmp; break; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER4: state=VER5; break; case L_VER: case VER1: case VER2: case VER3: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case '.': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP5: state=SIP6; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER5: state=VER6; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case '0': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP6: state=FIN_SIP; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: stat=stat*10; break; case L_STATUS: stat=0; fl->u.reply.status.s=tmp; break; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER6: state=FIN_VER; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'n': case 'N': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE1: state=INVITE2; break; case CANCEL2: state=CANCEL3; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'v': case 'V': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE2: state=INVITE3; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 't': case 'T': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE4: state=INVITE5; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'e': case 'E': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE5: state=FIN_INVITE; break; case CANCEL4: state=CANCEL5; break; case BYE2: state=FIN_BYE; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'a': case 'A': switch(state){ case START: state=ACK1; fl->u.request.method.s=tmp; break; case CANCEL1: state=CANCEL2; break; case BYE2: state=FIN_BYE; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'c': case 'C': switch(state){ case START: state=CANCEL1; fl->u.request.method.s=tmp; break; case CANCEL3: state=CANCEL4; break; case ACK1: state=ACK2; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'k': case 'K': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case ACK2: state=FIN_ACK; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'l': case 'L': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case CANCEL5: state=FIN_CANCEL; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'b': case 'B': switch(state){ case START: state=BYE1; fl->u.request.method.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'y': case 'Y': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case BYE1: state=BYE2; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case '\r': switch(state){ case P_REASON: *tmp=0; fl->u.reply.reason.len=tmp-fl->u.reply.reason.s; state=F_CR; break; case L_LF: state=F_CR; break; case FIN_VER: *tmp=0; fl->u.request.version.len=tmp-fl->u.request.version.s; state=F_CR; break; case L_REASON: state=F_CR; break; default: LOG(L_ERR, "ERROR: parse_first_line: invalid message\n"); goto error; } break; case '\n': switch(state){ case P_REASON: *tmp=0; fl->u.reply.reason.len=tmp-fl->u.reply.reason.s; state=F_LF; goto skip; case FIN_VER: *tmp=0; fl->u.request.version.len=tmp-fl->u.request.version.s; state=F_LF; goto skip; case L_REASON: case L_LF: case F_CR: state=F_LF; goto skip; default: LOG(L_ERR, "ERROR: parse_first_line: invalid message\n"); goto error; } break; case '1': case '3': case '4': case '5': case '6': case '7': case '8': case '9': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: stat=stat*10+*tmp-'0'; break; case L_STATUS: stat=*tmp-'0'; state=P_STATUS; fl->u.reply.status.s=tmp; break; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } default: switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LOG(L_ERR, "ERROR: parse_first_line: non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LOG(L_ERR, "ERROR: parse_first_line: invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; state=VER3; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LOG(L_ERR, "ERROR: parse_first_line: invalid version " " in request\n"); goto error; default: state=P_METHOD; } } } skip: if (fl->type==SIP_REPLY){ fl->u.reply.statuscode=stat; fl->u.reply.statusclass=stat/100; } return tmp; error: LOG(L_ERR, "ERROR: while parsing first line (state=%d)\n", state); fl->type=SIP_INVALID; return tmp; }