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); |