#include #include "sockutil.h" #include "aio-timeout.h" #include "sip-uac.h" #include "sip-message.h" #include "sip-transport.h" #include "sip-timer.h" #include "port/ip-route.h" #include "http-parser.h" #include "http-header-auth.h" #include "sys/thread.h" #include "sys/system.h" #include "uri-parse.h" #include "cstringext.h" #include "base64.h" #include "md5.h" #include struct sip_uac_test_t { socket_t udp; socklen_t addrlen; struct sockaddr_storage addr; http_parser_t* parser; struct sip_agent_t* sip; struct sip_transport_t transport; }; static int sip_uac_transport_via(void* transport, const char* destination, char protocol[16], char local[128], char dns[128]) { int r; char ip[65]; u_short port; struct uri_t* uri; struct sip_uac_test_t *test = (struct sip_uac_test_t *)transport; // rfc3263 4.1 Selecting a Transport Protocol // 1. If the URI specifies a transport protocol in the transport parameter, // that transport protocol SHOULD be used. Otherwise, if no transport // protocol is specified, but the TARGET(maddr) is a numeric IP address, // the client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI. // 2. if no transport protocol is specified, and the TARGET is not numeric, // but an explicit port is provided, the client SHOULD use UDP for a SIP URI, // and TCP for a SIPS URI // 3. Otherwise, if no transport protocol or port is specified, and the target // is not a numeric IP address, the client SHOULD perform a NAPTR query for // the domain in the URI. // The client SHOULD try the first record. If an attempt should fail, based on // the definition of failure in Section 4.3, the next SHOULD be tried, and if // that should fail, the next SHOULD be tried, and so on. test->addrlen = sizeof(test->addr); memset(&test->addr, 0, sizeof(test->addr)); strcpy(protocol, "UDP"); uri = uri_parse(destination, strlen(destination)); if (!uri) return -1; // invalid uri // rfc3263 4-Client Usage (p5) // once a SIP server has successfully been contacted (success is defined below), // all retransmissions of the SIP request and the ACK for non-2xx SIP responses // to INVITE MUST be sent to the same host. // Furthermore, a CANCEL for a particular SIP request MUST be sent to the same // SIP server that the SIP request was delivered to. // TODO: sips port r = socket_addr_from(&test->addr, &test->addrlen, uri->host, uri->port ? uri->port : SIP_PORT); if (0 == r) { socket_addr_to((struct sockaddr*)&test->addr, test->addrlen, ip, &port); socket_getname(test->udp, local, &port); r = ip_route_get(ip, local); if (0 == r) { dns[0] = 0; struct sockaddr_storage ss; socklen_t len = sizeof(ss); if (0 == socket_addr_from(&ss, &len, local, port)) socket_addr_name((struct sockaddr*)&ss, len, dns, 128); if (SIP_PORT != port) snprintf(local + strlen(local), 128 - strlen(local), ":%hu", port); if (NULL == strchr(dns, '.')) snprintf(dns, 128, "%s", local); // don't have valid dns } } uri_free(uri); return r; } static int sip_uac_transport_send(void* transport, const void* data, size_t bytes) { struct sip_uac_test_t *test = (struct sip_uac_test_t *)transport; //char p1[1024]; //char p2[1024]; ((char*)data)[bytes] = 0; printf("%s\n", (const char*)data); int r = socket_sendto(test->udp, data, bytes, 0, (struct sockaddr*)&test->addr, test->addrlen); return r == bytes ? 0 : -1; } static void sip_uac_recv_reply(struct sip_uac_test_t *test) { uint8_t buffer[2 * 1024]; struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); do { int r = socket_recvfrom(test->udp, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, &addrlen); size_t n = r; if (0 == http_parser_input(test->parser, buffer, &n)) { struct sip_message_t* reply = sip_message_create(SIP_MESSAGE_REPLY); r = sip_message_load(reply, test->parser); assert(0 == sip_agent_input(test->sip, reply, NULL)); sip_message_destroy(reply); http_parser_clear(test->parser); break; } } while (1); } static int sip_uac_onregister(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, int code) { char buffer[1024]; struct sip_uac_test_t *test = (struct sip_uac_test_t *)param; if (200 <= code && code < 300) { } else if (401 == code) { // https://blog.csdn.net/yunlianglinfeng/article/details/81109380 // http://www.voidcn.com/article/p-oqqbqgvd-bgn.html const char* h; std::shared_ptr t(sip_uac_register(test->sip, "sip:34020000001320000001@192.168.154.1", "sip:192.168.154.128", 3600, sip_uac_onregister, param), sip_uac_transaction_release); h = http_get_header_by_name(test->parser, "Call-ID"); sip_uac_add_header(t.get(), "Call-ID", h); // All registrations from a UAC SHOULD use the same Call-ID h = http_get_header_by_name(test->parser, "CSeq"); snprintf(buffer, sizeof(buffer), "%u REGISTER", atoi(h) + 1); sip_uac_add_header(t.get(), "CSeq", buffer); // A UA MUST increment the CSeq value by one for each REGISTER request with the same Call-ID // Unauthorized struct http_header_www_authenticate_t auth; memset(&auth, 0, sizeof(auth)); h = http_get_header_by_name(test->parser, "WWW-Authenticate"); assert(0 == http_header_www_authenticate(h, &auth)); switch (auth.scheme) { case HTTP_AUTHENTICATION_DIGEST: //HA1=md5(username:realm:password) //HA2=md5(Method:Uri) //RESPONSE=md5(HA1:nonce:HA2) ++auth.nc; if(!auth.algorithm[0]) snprintf(auth.algorithm, sizeof(auth.algorithm), "MD5"); if (!auth.qop[0]) snprintf(auth.qop, sizeof(auth.qop), "auth"); else if (strchr(auth.qop, ',')) *strchr(auth.qop, ',') = '\0'; // replace auth,auth-int -> auth snprintf(auth.uri, sizeof(auth.uri), "sip:%s", "192.168.154.128"); snprintf(auth.username, sizeof(auth.username), "%s", "34020000001320000001"); http_header_auth(&auth, "12345678", "REGISTER", NULL, 0, buffer, sizeof(buffer)); sip_uac_add_header(t.get(), "Authorization", buffer); assert(0 == sip_uac_send(t.get(), NULL, 0, &test->transport, test)); sip_uac_recv_reply(test); break; case HTTP_AUTHENTICATION_BASIC: break; default: assert(0); } } else { } return 0; } static void sip_uac_register_test(struct sip_uac_test_t *test) { //t = sip_uac_register(uac, "Bob ", "sip:registrar.biloxi.com", 7200, sip_uac_message_onregister, test); std::shared_ptr t(sip_uac_register(test->sip, "sip:34020000001320000001@192.168.154.1", "sip:192.168.154.128", 3600, sip_uac_onregister, test), sip_uac_transaction_release); assert(0 == sip_uac_send(t.get(), NULL, 0, &test->transport, test)); sip_uac_recv_reply(test); } static int sip_uac_onmessage(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, int code) { return 0; } static void sip_uac_message_test(struct sip_uac_test_t *test) { const char* msg = "" "" "Keepalive" "478" "34020000001320000001" "OK" ""; std::shared_ptr t(sip_uac_message(test->sip, "sip:34020000001320000001@192.168.154.1", "sip:34020000002000000001@192.168.154.128", sip_uac_onmessage, test), sip_uac_transaction_release); sip_uac_add_header(t.get(), "Content-Type", "Application/MANSCDP+xml"); assert(0 == sip_uac_send(t.get(), msg, strlen(msg), &test->transport, test)); sip_uac_recv_reply(test); } static int sip_uac_oninvited(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, struct sip_dialog_t* dialog, int code, void** session) { if (200 <= code && code < 300) { *session = NULL; sip_uac_ack(t, NULL, 0); } return NULL; } static void sip_uac_invite_test(struct sip_uac_test_t *test) { std::shared_ptr t(sip_uac_invite(test->sip, "sip:34020000001320000001@192.168.154.128", "sip:34020000001320000001@192.168.154.128", sip_uac_oninvited, test), sip_uac_transaction_release); assert(0 == sip_uac_send(t.get(), NULL, 0, &test->transport, test)); sip_uac_recv_reply(test); } void sip_uac_test(void) { sip_timer_init(); struct sip_uac_test_t test; test.transport = { sip_uac_transport_via, sip_uac_transport_send, }; struct sip_uas_handler_t handler; memset(&handler, 0, sizeof(handler)); test.udp = socket_udp(); test.sip = sip_agent_create(&handler); test.parser = http_parser_create(HTTP_PARSER_RESPONSE, NULL, NULL); socket_bind_any(test.udp, SIP_PORT); sip_uac_register_test(&test); sip_uac_message_test(&test); //sip_uac_invite_test(&test); sip_agent_destroy(test.sip); socket_close(test.udp); http_parser_destroy(test.parser); sip_timer_cleanup(); }