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