Mercurial > pidgin
comparison src/protocols/simple/simple.c @ 11189:5f79dfde334c
[gaim-migrate @ 13307]
added UDP transport for SIP/SIMPLE
committer: Tailor Script <tailor@pidgin.im>
| author | Thomas Butter <tbutter> |
|---|---|
| date | Thu, 04 Aug 2005 09:10:11 +0000 |
| parents | e5bbe5070e04 |
| children | fde0f4c1348d |
comparison
equal
deleted
inserted
replaced
| 11188:12fc7a3fbc88 | 11189:5f79dfde334c |
|---|---|
| 31 #include "plugin.h" | 31 #include "plugin.h" |
| 32 #include "util.h" | 32 #include "util.h" |
| 33 #include "version.h" | 33 #include "version.h" |
| 34 #include "network.h" | 34 #include "network.h" |
| 35 #include "xmlnode.h" | 35 #include "xmlnode.h" |
| 36 #include "stun.h" | |
| 36 | 37 |
| 37 #include "simple.h" | 38 #include "simple.h" |
| 38 #include "sipmsg.h" | 39 #include "sipmsg.h" |
| 39 #include "srvresolve.h" | 40 #include "srvresolve.h" |
| 40 | 41 |
| 257 static void sendlater(GaimConnection *gc, const char *buf) { | 258 static void sendlater(GaimConnection *gc, const char *buf) { |
| 258 struct getserver_return *serveradr; | 259 struct getserver_return *serveradr; |
| 259 struct simple_account_data *sip = gc->proto_data; | 260 struct simple_account_data *sip = gc->proto_data; |
| 260 int error = 0; | 261 int error = 0; |
| 261 if(!sip->connecting) { | 262 if(!sip->connecting) { |
| 262 serveradr = getserver(sip->servername); | 263 serveradr = getserver(sip->servername, "_sip._tcp"); |
| 263 gaim_debug_info("simple","connecting to %s port %d", serveradr->name, serveradr->port); | 264 gaim_debug_info("simple","connecting to %s port %d", serveradr->name, serveradr->port); |
| 264 error = gaim_proxy_connect(sip->account, serveradr->name, serveradr->port, send_later_cb, gc); | 265 error = gaim_proxy_connect(sip->account, serveradr->name, serveradr->port, send_later_cb, gc); |
| 265 if(error) { | 266 if(error) { |
| 266 gaim_connection_error(gc, _("Couldn't create socket")); | 267 gaim_connection_error(gc, _("Couldn't create socket")); |
| 267 } | 268 } |
| 276 } | 277 } |
| 277 | 278 |
| 278 static int sendout_pkt(GaimConnection *gc, const char *buf) { | 279 static int sendout_pkt(GaimConnection *gc, const char *buf) { |
| 279 struct simple_account_data *sip = gc->proto_data; | 280 struct simple_account_data *sip = gc->proto_data; |
| 280 time_t currtime = time(NULL); | 281 time_t currtime = time(NULL); |
| 281 int ret; | 282 int ret = 0; |
| 282 | 283 |
| 283 gaim_debug(GAIM_DEBUG_MISC, "simple", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf); | 284 gaim_debug(GAIM_DEBUG_MISC, "simple", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf); |
| 284 if(sip->fd <0 ) { | 285 if(sip->udp) { |
| 285 sendlater(gc, buf); | 286 if(sendto(sip->fd, buf, strlen(buf), 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < strlen(buf)) { |
| 286 return 0; | 287 gaim_debug_info("simple", "could not send packet\n"); |
| 287 } | 288 } |
| 288 ret = write(sip->fd, buf, strlen(buf)); | 289 } else { |
| 289 if(ret < 0) { | 290 if(sip->fd <0 ) { |
| 290 sendlater(gc,buf); | 291 sendlater(gc, buf); |
| 291 return 0; | 292 return 0; |
| 293 } | |
| 294 ret = write(sip->fd, buf, strlen(buf)); | |
| 295 if(ret < 0) { | |
| 296 sendlater(gc,buf); | |
| 297 return 0; | |
| 298 } | |
| 292 } | 299 } |
| 293 return ret; | 300 return ret; |
| 294 } | 301 } |
| 295 | 302 |
| 296 static void send_sip_response(GaimConnection *gc, struct sipmsg *msg, int code, char *text, char *body) { | 303 static void send_sip_response(GaimConnection *gc, struct sipmsg *msg, int code, char *text, char *body) { |
| 414 struct simple_account_data *sip = gc->proto_data; | 421 struct simple_account_data *sip = gc->proto_data; |
| 415 sip->registerstatus = 1; | 422 sip->registerstatus = 1; |
| 416 | 423 |
| 417 char *uri = g_strdup_printf("sip:%s",sip->servername); | 424 char *uri = g_strdup_printf("sip:%s",sip->servername); |
| 418 char *to = g_strdup_printf("sip:%s@%s",sip->username,sip->servername); | 425 char *to = g_strdup_printf("sip:%s@%s",sip->username,sip->servername); |
| 419 char *contact = g_strdup_printf("Contact: <sip:%s@%s:%d;transport=tcp>;methods=\"MESSAGE, SUBSCRIBE, NOTIFY\"\r\nExpires: 900\r\n", sip->username, sip->ip, sip->listenport); | 426 char *contact = g_strdup_printf("Contact: <sip:%s@%s:%d;transport=%s>;methods=\"MESSAGE, SUBSCRIBE, NOTIFY\"\r\nExpires: 900\r\n", sip->username, sip->ip, sip->listenport, sip->udp ? "udp" : "tcp"); |
| 420 | 427 |
| 421 // allow one auth try per register | 428 // allow one auth try per register |
| 422 sip->proxy.fouroseven = 0; | 429 sip->proxy.fouroseven = 0; |
| 423 sip->registrar.fouroseven = 0; | 430 sip->registrar.fouroseven = 0; |
| 424 | 431 |
| 802 send_sip_response(sip->gc, msg, 200, "Ok", NULL); | 809 send_sip_response(sip->gc, msg, 200, "Ok", NULL); |
| 803 g_free(tmp); | 810 g_free(tmp); |
| 804 send_notify(sip, watcher); | 811 send_notify(sip, watcher); |
| 805 } | 812 } |
| 806 | 813 |
| 814 static void process_input_message(struct simple_account_data *sip, struct sipmsg *msg) { | |
| 815 int found = 0; | |
| 816 if( msg->response == 0 ) { // request | |
| 817 if(!strcmp(msg->method, "MESSAGE")) { | |
| 818 process_incoming_message(sip, msg); | |
| 819 found = 1; | |
| 820 } | |
| 821 if(!strcmp(msg->method, "NOTIFY")) { | |
| 822 process_incoming_notify(sip, msg); | |
| 823 found = 1; | |
| 824 } | |
| 825 if(!strcmp(msg->method, "SUBSCRIBE")) { | |
| 826 process_incoming_subscribe(sip, msg); | |
| 827 found = 1; | |
| 828 } | |
| 829 } else { // response | |
| 830 struct transaction *trans = transactions_find(sip, msg); | |
| 831 if(trans) { | |
| 832 if(msg->response == 407) { | |
| 833 if(sip->proxy.fouroseven>3) return; | |
| 834 sip->proxy.fouroseven++; | |
| 835 // do proxy authentication | |
| 836 | |
| 837 gchar *ptmp = sipmsg_find_header(msg,"Proxy-Authenticate"); | |
| 838 gchar *resend; | |
| 839 gchar *auth; | |
| 840 | |
| 841 HASHHEX HA2; | |
| 842 HASHHEX response; | |
| 843 gchar noncecount[90]; | |
| 844 fill_auth(sip, ptmp, &sip->proxy); | |
| 845 sprintf(noncecount, "%08d", sip->proxy.nc++); | |
| 846 | |
| 847 DigestCalcResponse(sip->proxy.HA1, sip->proxy.nonce, noncecount, "", "", trans->msg->method, trans->msg->target, HA2, response); | |
| 848 gaim_debug(GAIM_DEBUG_MISC, "simple", "response %s\n", response); | |
| 849 auth = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n",sip->username, sip->proxy.realm, sip->proxy.nonce, trans->msg->target, noncecount, response); | |
| 850 sipmsg_remove_header(msg, "Proxy-Authorization"); | |
| 851 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth); | |
| 852 g_free(auth); | |
| 853 resend = sipmsg_to_string(trans->msg); | |
| 854 // resend request | |
| 855 sendout_pkt(sip->gc, resend); | |
| 856 g_free(resend); | |
| 857 } else { | |
| 858 sip->proxy.fouroseven = 0; | |
| 859 if(msg->response == 401) sip->registrar.fouroseven++; | |
| 860 else sip->registrar.fouroseven = 0; | |
| 861 if(trans->callback) { | |
| 862 // call the callback to process response | |
| 863 (trans->callback)(sip, msg, trans); | |
| 864 sip->transactions = g_slist_remove(sip->transactions, trans); | |
| 865 } else { | |
| 866 // transaction has no callback - just remove it | |
| 867 sip->transactions = g_slist_remove(sip->transactions, trans); | |
| 868 } | |
| 869 } | |
| 870 found = 1; | |
| 871 } else { | |
| 872 gaim_debug(GAIM_DEBUG_MISC, "simple", "received response to unknown transaction"); | |
| 873 } | |
| 874 } | |
| 875 if(!found) { | |
| 876 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a unknown sip message with method %sand response %d\n",msg->method, msg->response); | |
| 877 } | |
| 878 } | |
| 879 | |
| 807 static void process_input(struct simple_account_data *sip, struct sip_connection *conn) | 880 static void process_input(struct simple_account_data *sip, struct sip_connection *conn) |
| 808 { | 881 { |
| 809 char *cur; | 882 char *cur; |
| 810 char *dummy; | 883 char *dummy; |
| 811 struct sipmsg *msg; | 884 struct sipmsg *msg; |
| 812 int restlen; | 885 int restlen; |
| 813 int found=0; | |
| 814 | 886 |
| 815 cur = conn->inbuf; | 887 cur = conn->inbuf; |
| 816 | 888 |
| 817 // according to the RFC remove CRLF at the beginning | 889 // according to the RFC remove CRLF at the beginning |
| 818 while(*cur == '\r' || *cur == '\n') { | 890 while(*cur == '\r' || *cur == '\n') { |
| 844 } else { | 916 } else { |
| 845 sipmsg_free(msg); | 917 sipmsg_free(msg); |
| 846 return; | 918 return; |
| 847 } | 919 } |
| 848 // sipmsg_print(msg); | 920 // sipmsg_print(msg); |
| 849 gaim_debug(GAIM_DEBUG_MISC, "simple", "in process response response: %d\n", msg->response); | 921 gaim_debug(GAIM_DEBUG_MISC, "simple", "in process response response: %d\n", msg->response); |
| 850 if( msg->response == 0 ) { // request | 922 process_input_message(sip,msg); |
| 851 if(!strcmp(msg->method, "MESSAGE")) { | |
| 852 process_incoming_message(sip, msg); | |
| 853 found = 1; | |
| 854 } | |
| 855 if(!strcmp(msg->method, "NOTIFY")) { | |
| 856 process_incoming_notify(sip, msg); | |
| 857 found = 1; | |
| 858 } | |
| 859 if(!strcmp(msg->method, "SUBSCRIBE")) { | |
| 860 process_incoming_subscribe(sip, msg); | |
| 861 found = 1; | |
| 862 } | |
| 863 } else { // response | |
| 864 struct transaction *trans = transactions_find(sip, msg); | |
| 865 if(trans) { | |
| 866 if(msg->response == 407) { | |
| 867 if(sip->proxy.fouroseven>3) return; | |
| 868 sip->proxy.fouroseven++; | |
| 869 // do proxy authentication | |
| 870 | |
| 871 gchar *ptmp = sipmsg_find_header(msg,"Proxy-Authenticate"); | |
| 872 gchar *resend; | |
| 873 gchar *auth; | |
| 874 | |
| 875 HASHHEX HA2; | |
| 876 HASHHEX response; | |
| 877 gchar noncecount[90]; | |
| 878 fill_auth(sip, ptmp, &sip->proxy); | |
| 879 sprintf(noncecount, "%08d", sip->proxy.nc++); | |
| 880 | |
| 881 DigestCalcResponse(sip->proxy.HA1, sip->proxy.nonce, noncecount, "", "", trans->msg->method, trans->msg->target, HA2, response); | |
| 882 gaim_debug(GAIM_DEBUG_MISC, "simple", "response %s\n", response); | |
| 883 auth = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n",sip->username, sip->proxy.realm, sip->proxy.nonce, trans->msg->target, noncecount, response); | |
| 884 sipmsg_remove_header(msg, "Proxy-Authorization"); | |
| 885 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth); | |
| 886 g_free(auth); | |
| 887 resend = sipmsg_to_string(trans->msg); | |
| 888 // resend request | |
| 889 sendout_pkt(sip->gc, resend); | |
| 890 g_free(resend); | |
| 891 } else { | |
| 892 sip->proxy.fouroseven = 0; | |
| 893 if(msg->response == 401) sip->registrar.fouroseven++; | |
| 894 else sip->registrar.fouroseven = 0; | |
| 895 if(trans->callback) { | |
| 896 // call the callback to process response | |
| 897 (trans->callback)(sip, msg, trans); | |
| 898 | |
| 899 sip->transactions = g_slist_remove(sip->transactions, trans); | |
| 900 } else { | |
| 901 // transaction has no callback - just remove it | |
| 902 sip->transactions = g_slist_remove(sip->transactions, trans); | |
| 903 } | |
| 904 } | |
| 905 found = 1; | |
| 906 } else { | |
| 907 gaim_debug(GAIM_DEBUG_MISC, "simple", "received response to unknown transaction"); | |
| 908 } | |
| 909 } | |
| 910 if(!found) { | |
| 911 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a unknown sip message with method %sand response %d\n",msg->method, msg->response); | |
| 912 } | |
| 913 } else { | 923 } else { |
| 914 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a incomplete sip msg: %s\n", conn->inbuf); | 924 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a incomplete sip msg: %s\n", conn->inbuf); |
| 915 } | 925 } |
| 926 } | |
| 927 | |
| 928 static void simple_udp_process(gpointer data, gint source, GaimInputCondition con) { | |
| 929 GaimConnection *gc = data; | |
| 930 struct simple_account_data *sip = gc->proto_data; | |
| 931 struct sipmsg *msg; | |
| 932 int len; | |
| 933 time_t currtime; | |
| 934 | |
| 935 static char buffer[65536]; | |
| 936 len = recv(source, buffer, 65536, 0); | |
| 937 buffer[len] = 0; | |
| 938 gaim_debug_info("simple","\n\nreceived - %s\n######\n%s\n#######\n\n",ctime(&currtime), buffer); | |
| 939 msg = sipmsg_parse_msg(buffer); | |
| 940 if(msg) process_input_message(sip, msg); | |
| 916 } | 941 } |
| 917 | 942 |
| 918 static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond) | 943 static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond) |
| 919 { | 944 { |
| 920 GaimConnection *gc = data; | 945 GaimConnection *gc = data; |
| 1013 | 1038 |
| 1014 gc = gaim_account_get_connection(account); | 1039 gc = gaim_account_get_connection(account); |
| 1015 | 1040 |
| 1016 gc->proto_data = sip = g_new0(struct simple_account_data,1); | 1041 gc->proto_data = sip = g_new0(struct simple_account_data,1); |
| 1017 sip->gc=gc; | 1042 sip->gc=gc; |
| 1018 sip->account = account; | 1043 sip->account = account; |
| 1044 | |
| 1045 sip->udp = gaim_account_get_bool(account, "udp", FALSE); | |
| 1019 if (strpbrk(username, " \t\v\r\n") != NULL) { | 1046 if (strpbrk(username, " \t\v\r\n") != NULL) { |
| 1020 gaim_connection_error(gc, _("SIP usernames may not contain whitespaces or @ symbols")); | 1047 gaim_connection_error(gc, _("SIP usernames may not contain whitespaces or @ symbols")); |
| 1021 return; | 1048 return; |
| 1022 } | 1049 } |
| 1023 | 1050 |
| 1032 sip->buddies = g_hash_table_new((GHashFunc)simple_ht_hash_nick, (GEqualFunc)simple_ht_equals_nick); | 1059 sip->buddies = g_hash_table_new((GHashFunc)simple_ht_hash_nick, (GEqualFunc)simple_ht_equals_nick); |
| 1033 | 1060 |
| 1034 gaim_connection_update_progress(gc, _("Connecting"), 1, 2); | 1061 gaim_connection_update_progress(gc, _("Connecting"), 1, 2); |
| 1035 | 1062 |
| 1036 sip->status = g_strdup("available"); | 1063 sip->status = g_strdup("available"); |
| 1037 | 1064 |
| 1038 // search for SRV record | 1065 // TCP case |
| 1039 serveradr = getserver(sip->servername); | 1066 if(! sip->udp) { |
| 1040 gaim_debug_info("simple","connecting to %s port %d", serveradr->name, serveradr->port); | 1067 // search for SRV record |
| 1041 | 1068 serveradr = getserver(sip->servername, "_sip._tcp"); |
| 1042 // open tcp connection to the server | 1069 gaim_debug_info("simple","connecting to %s port %d", serveradr->name, serveradr->port); |
| 1043 error = gaim_proxy_connect(account, serveradr->name, serveradr->port, login_cb, gc); | 1070 |
| 1044 if(error) { | 1071 // open tcp connection to the server |
| 1045 gaim_connection_error(gc, _("Couldn't create socket")); | 1072 error = gaim_proxy_connect(account, serveradr->name, serveradr->port, login_cb, gc); |
| 1046 } | 1073 if(error) { |
| 1047 | 1074 gaim_connection_error(gc, _("Couldn't create socket")); |
| 1048 // create socket for incoming connections | 1075 } |
| 1049 sip->listenfd = gaim_network_listen_range(5060, 5080); | 1076 |
| 1050 if(sip->listenfd == -1) { | 1077 // create socket for incoming connections |
| 1051 gaim_connection_error(gc, _("Could not create listen socket")); | 1078 sip->listenfd = gaim_network_listen_range(5060, 5080); |
| 1052 return; | 1079 if(sip->listenfd == -1) { |
| 1053 } | 1080 gaim_connection_error(gc, _("Could not create listen socket")); |
| 1054 sip->listenport = gaim_network_get_port_from_fd(sip->listenfd); | 1081 return; |
| 1055 gaim_input_add(sip->listenfd, GAIM_INPUT_READ, simple_newconn_cb, gc); | 1082 } |
| 1056 | 1083 sip->listenport = gaim_network_get_port_from_fd(sip->listenfd); |
| 1084 gaim_input_add(sip->listenfd, GAIM_INPUT_READ, simple_newconn_cb, gc); | |
| 1085 } else { // UDP | |
| 1086 // search for SRV record | |
| 1087 struct sockaddr_in addr; | |
| 1088 struct hostent *h; | |
| 1089 | |
| 1090 serveradr = getserver(sip->servername, "_sip._udp"); | |
| 1091 gaim_debug_info("simple", "using udp with server %s and port %d", serveradr->name, serveradr->port); | |
| 1092 sip->fd = socket(AF_INET, SOCK_DGRAM, 0); | |
| 1093 | |
| 1094 addr.sin_family = AF_INET; | |
| 1095 addr.sin_port = htons(5060); | |
| 1096 addr.sin_addr.s_addr = INADDR_ANY; | |
| 1097 while((bind(sip->fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) <0) && ntohs(addr.sin_port)<5160) { | |
| 1098 addr.sin_port = htons(ntohs(addr.sin_port)+1); | |
| 1099 } | |
| 1100 sip->listenport = ntohs(addr.sin_port); | |
| 1101 sip->listenfd = sip->fd; | |
| 1102 | |
| 1103 gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_udp_process, gc); | |
| 1104 // TODO - change to new SRV impl. | |
| 1105 sip->serveraddr.sin_family = AF_INET; | |
| 1106 sip->serveraddr.sin_port = htons(serveradr->port); | |
| 1107 | |
| 1108 h = gethostbyname(serveradr->name); | |
| 1109 sip->serveraddr.sin_addr.s_addr = ((struct in_addr*)h->h_addr)->s_addr; | |
| 1110 sip->ip = g_strdup(gaim_network_get_my_ip(sip->listenfd)); | |
| 1111 | |
| 1112 do_register(gc); | |
| 1113 | |
| 1114 } | |
| 1115 | |
| 1057 // register timeout callback for register / subscribe renewal | 1116 // register timeout callback for register / subscribe renewal |
| 1058 sip->registertimeout = gaim_timeout_add((rand()%10)+10*1000, (GSourceFunc)register_timeout, sip); | 1117 sip->registertimeout = gaim_timeout_add((rand()%10)+10*1000, (GSourceFunc)register_timeout, sip); |
| 1059 } | 1118 } |
| 1060 | 1119 |
| 1061 static void simple_close(GaimConnection *gc) | 1120 static void simple_close(GaimConnection *gc) |
| 1173 }; | 1232 }; |
| 1174 | 1233 |
| 1175 static void _init_plugin(GaimPlugin *plugin) | 1234 static void _init_plugin(GaimPlugin *plugin) |
| 1176 { | 1235 { |
| 1177 GaimAccountUserSplit *split; | 1236 GaimAccountUserSplit *split; |
| 1237 GaimAccountOption *option; | |
| 1178 | 1238 |
| 1179 gaim_debug_register_category("simple"); | 1239 gaim_debug_register_category("simple"); |
| 1180 | 1240 |
| 1181 split = gaim_account_user_split_new(_("Server"), "blubb.com", '@'); | 1241 split = gaim_account_user_split_new(_("Server"), "blubb.com", '@'); |
| 1182 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); | 1242 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); |
| 1183 | 1243 |
| 1244 option = gaim_account_option_bool_new(_("Use UDP"), "udp", FALSE); | |
| 1245 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1184 // _simple_plugin = plugin; | 1246 // _simple_plugin = plugin; |
| 1185 } | 1247 } |
| 1186 | 1248 |
| 1187 GAIM_INIT_PLUGIN(simple, _init_plugin, info); | 1249 GAIM_INIT_PLUGIN(simple, _init_plugin, info); |
