PageRenderTime 25ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/xconvers-0.8.3/src/net.c

#
C | 458 lines | 314 code | 64 blank | 80 comment | 43 complexity | 0462d0b0a6b9966d4b99ca11b7ddca4e MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * xconvers - GTK+ convers client for amateur radio
  3. * Copyright (C) 2000-2003 Joop Stakenborg <pg4i@amsat.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Library General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. /*
  20. * net.c - private functions for sending and receiving data, connecting and
  21. * disconnecting.
  22. */
  23. #include <stdlib.h>
  24. #include <unistd.h>
  25. #include <string.h>
  26. #include <errno.h>
  27. #include <resolv.h>
  28. #include <netdb.h>
  29. #include <sys/socket.h>
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <sys/types.h>
  33. #include <sys/wait.h>
  34. #include <signal.h>
  35. #include <fcntl.h>
  36. #include "support.h"
  37. #include "net.h"
  38. #include "types.h"
  39. #include "utils.h"
  40. gint sockethandle;
  41. gboolean connecttimeout = FALSE;
  42. extern GHashTable *callsigns;
  43. extern GtkWidget *mainwindow;
  44. extern gpointer cvhost, cvport;
  45. extern gchar *preferencesdir, *logfilename;
  46. extern gint rxmonitor, usercolor;
  47. extern gboolean connected, locked;
  48. extern FILE *logfp;
  49. extern preferencestype preferences;
  50. static gint resolvepipe[2], resolvepid, resolvemonitor = 0, connectmonitor,
  51. progresstimer;
  52. /*
  53. * Handler for connection timeout, set to 90 seconds.
  54. */
  55. static void connectalarmhandler()
  56. {
  57. GString *msg = g_string_new(_("Time-out during connection setup"));
  58. GtkWidget *menuopen, *menuclose;
  59. /* ignore pending alarms and cleanup */
  60. signal(SIGALRM, SIG_IGN);
  61. alarm(0);
  62. updatestatusbar(msg);
  63. g_string_free(msg, TRUE);
  64. gtk_timeout_remove(progresstimer);
  65. connecttimeout = TRUE;
  66. close(sockethandle);
  67. menuclose = lookup_widget(mainwindow, "close");
  68. menuopen = lookup_widget(mainwindow, "open");
  69. gtk_widget_set_sensitive(menuopen, 1);
  70. gtk_widget_set_sensitive(menuclose, 0);
  71. return;
  72. }
  73. /*
  74. * Free the child if it is a zombie. A returned FALSE value will stop the
  75. * timer which calls this function every second.
  76. */
  77. static gboolean childcheck() {
  78. gint status, childpid;
  79. childpid = waitpid(-1, &status, WNOHANG);
  80. return (WIFEXITED(status) == 0);
  81. }
  82. /*
  83. * Read result from the child through the pipe.
  84. */
  85. static void resolvereadpipe()
  86. {
  87. gint error = 1, ret, len;
  88. gchar *resultstr = NULL;
  89. GString *msg = g_string_new("");
  90. GtkWidget *menuclose, *menuopen;
  91. /* resolving has returned a result, we can stop the monitor */
  92. gdk_input_remove(resolvemonitor);
  93. /* read result of connection from the pipe */
  94. ret = read(resolvepipe[0], &error, sizeof(error));
  95. if (error != 1)
  96. {
  97. /* we first receive length of string, than the string itself */
  98. ret = read(resolvepipe[0], &len, sizeof(len));
  99. resultstr = g_malloc0(len);
  100. ret = read(resolvepipe[0], resultstr, len);
  101. ret = close(resolvepipe[0]);
  102. ret = close(resolvepipe[1]);
  103. if (error == -1)
  104. {
  105. g_string_sprintf(msg, _("Resolver failed: %s"), resultstr);
  106. updatestatusbar(msg);
  107. menuclose = lookup_widget(mainwindow, "close");
  108. menuopen = lookup_widget(mainwindow, "open");
  109. gtk_widget_set_sensitive(menuopen, 1);
  110. gtk_widget_set_sensitive(menuclose, 0);
  111. gtk_timeout_remove(progresstimer);
  112. }
  113. else
  114. {
  115. cvconnect(resultstr);
  116. }
  117. g_string_free(msg, TRUE);
  118. g_free(resultstr);
  119. }
  120. }
  121. /*
  122. * Move the progressbar by adding 1.
  123. */
  124. static gint progress_timeout(gpointer data)
  125. {
  126. gfloat new_val;
  127. GtkAdjustment *adj;
  128. new_val = gtk_progress_get_value(GTK_PROGRESS(data)) + 1;
  129. adj = GTK_PROGRESS(data)->adjustment;
  130. if (new_val > adj->upper) new_val = adj->lower;
  131. gtk_progress_set_value (GTK_PROGRESS (data), new_val);
  132. return(TRUE);
  133. }
  134. /*
  135. * Resolving of the hostname trough a child. The result is sent over a pipe.
  136. * This way we don't block any dialogs.
  137. */
  138. void cvresolve(void)
  139. {
  140. gint error;
  141. GString *msg = g_string_new("");
  142. GtkWidget *progressbar;
  143. error = pipe(resolvepipe);
  144. if (error != 0)
  145. {
  146. g_string_sprintf(msg, _("Pipe failed: %s"), g_strerror(errno));
  147. updatestatusbar(msg);
  148. g_string_free(msg, TRUE);
  149. return;
  150. }
  151. g_string_sprintf(msg, _("Resolving %s..."), (gchar *)cvhost);
  152. updatestatusbar(msg);
  153. g_string_free(msg, TRUE);
  154. progressbar = lookup_widget(mainwindow, "progressbar");
  155. progresstimer = gtk_timeout_add (50, progress_timeout, progressbar);
  156. resolvepid = resolvenonblock();
  157. if (resolvepid == -1) {
  158. g_string_sprintf(msg, _("Fork failed: %s"), g_strerror(errno));
  159. updatestatusbar(msg);
  160. g_string_free(msg, TRUE);
  161. return;
  162. }
  163. /* monitor one end of the pipe for the result */
  164. resolvemonitor = gdk_input_add(resolvepipe[0], GDK_INPUT_READ, GTK_SIGNAL_FUNC(resolvereadpipe), NULL);
  165. /* check every second if the child has finished until childcheck returns FALSE, so the timer will be stopped */
  166. (void)gtk_timeout_add(1000, childcheck, NULL);
  167. }
  168. /*
  169. * Here the actual child is created. Both resolving errors and results are
  170. * written through the pipe.
  171. */
  172. gint resolvenonblock(void)
  173. {
  174. gint pid, len, ret, error = 0;
  175. struct hostent *cvhostent;
  176. /* create fork */
  177. pid = fork();
  178. /* use child for resolving */
  179. if (pid == 0)
  180. {
  181. cvhostent = gethostbyname(cvhost);
  182. if (cvhostent == NULL)
  183. { /* resolving failed, write result to pipe */
  184. error = -1;
  185. ret = write(resolvepipe[1], &error, sizeof(error));
  186. len = strlen(hstrerror(h_errno)) + 1;
  187. ret = write(resolvepipe[1], &len, sizeof(len));
  188. ret = write(resolvepipe[1], hstrerror(h_errno), len);
  189. }
  190. else
  191. { /* send IP-address over the pipe */
  192. ret = write(resolvepipe[1], &error, sizeof(error));
  193. len = cvhostent->h_length;
  194. ret = write(resolvepipe[1], &len, sizeof(len));
  195. ret = write(resolvepipe[1], cvhostent->h_addr, len);
  196. }
  197. _exit(0);
  198. }
  199. return(pid); /* parent pid */
  200. }
  201. /*
  202. * Something has happened on the socket. We now have to check if a connection
  203. * has succeeded. Do autologin if enabled and setup a monitor for incoming
  204. * packets.
  205. */
  206. static void socket_connected(void)
  207. {
  208. GtkWidget *menuopen, *menuclose;
  209. GString *msg = g_string_new(""), *sendstring;
  210. gchar **sendsplit = NULL;
  211. gint i = 0, res, option, size = sizeof(gint);
  212. gdk_input_remove(connectmonitor);
  213. /* Disable alarm handler */
  214. signal(SIGALRM, SIG_IGN);
  215. alarm(0);
  216. res = getsockopt(sockethandle, SOL_SOCKET, SO_ERROR, &option, &size);
  217. if (option != 0)
  218. {
  219. if (!connecttimeout)
  220. {
  221. msg = g_string_new(strerror(option));
  222. updatestatusbar(msg);
  223. g_string_free(msg, TRUE);
  224. menuclose = lookup_widget(mainwindow, "close");
  225. menuopen = lookup_widget(mainwindow, "open");
  226. gtk_widget_set_sensitive(menuopen, 1);
  227. gtk_widget_set_sensitive(menuclose, 0);
  228. gtk_timeout_remove(progresstimer);
  229. }
  230. return;
  231. }
  232. g_string_sprintf(msg, _("Connected to %s"), (gchar *)cvhost);
  233. updatestatusbar(msg);
  234. g_string_free(msg, TRUE);
  235. menuclose = lookup_widget(mainwindow, "close");
  236. menuopen = lookup_widget(mainwindow, "open");
  237. gtk_widget_set_sensitive(menuopen, 0);
  238. gtk_widget_set_sensitive(menuclose, 1);
  239. if (preferences.logging)
  240. {
  241. logfilename = g_strconcat(preferencesdir, "/", cvhost, ".log", NULL);
  242. if ((logfp = fopen(logfilename, "w")) == NULL)
  243. g_error(_("Creating %s for writing."), logfilename);
  244. }
  245. if (preferences.autologin && preferences.name)
  246. {
  247. sendstring = g_string_new("/n ");
  248. g_string_append(sendstring, preferences.name->str);
  249. if (preferences.channel)
  250. {
  251. g_string_append(sendstring, " ");
  252. g_string_append(sendstring, preferences.channel->str);
  253. }
  254. tx(sendstring);
  255. g_string_free(sendstring, TRUE);
  256. }
  257. if (preferences.autologin && preferences.commands)
  258. {
  259. sendsplit = g_strsplit(preferences.commands->str, ";", 0);
  260. while (sendsplit[i])
  261. {
  262. sendstring = g_string_new(sendsplit[i]);
  263. tx(sendstring);
  264. g_string_free(sendstring, TRUE);
  265. i++;
  266. }
  267. g_strfreev(sendsplit);
  268. }
  269. gtk_timeout_remove(progresstimer);
  270. rxmonitor = gdk_input_add(sockethandle, GDK_INPUT_READ, GTK_SIGNAL_FUNC(rx), NULL);
  271. callsigns = g_hash_table_new(g_str_hash, g_str_equal);
  272. connected = TRUE;
  273. }
  274. /*
  275. * Connect routine. Create the socket and recover from possible errors,
  276. * which will be displayed in the statusbar. Also, set up a monitor which
  277. * checks if the connection is created.
  278. */
  279. void cvconnect(gchar *where)
  280. {
  281. GString *msg = g_string_new("");
  282. gint opt, ret;
  283. struct sockaddr_in cvaddress;
  284. g_string_sprintf(msg, _("Connecting to %s..."), inet_ntoa(*((struct in_addr *)where)));
  285. updatestatusbar(msg);
  286. g_string_free(msg, TRUE);
  287. if ((sockethandle = socket (AF_INET, SOCK_STREAM, 0)) == -1)
  288. {
  289. msg = g_string_new(strerror(errno));
  290. updatestatusbar(msg);
  291. g_string_free(msg, TRUE);
  292. return;
  293. }
  294. /* send keepalive probes */
  295. setsockopt(sockethandle, SOL_SOCKET, SO_KEEPALIVE, (gchar *)&opt, sizeof(opt));
  296. /* make connection setup non-blocking */
  297. fcntl(sockethandle, F_SETFL, O_NONBLOCK);
  298. cvaddress.sin_family = AF_INET;
  299. cvaddress.sin_port = htons(atoi(cvport));
  300. cvaddress.sin_addr = *((struct in_addr *)where);
  301. bzero (&(cvaddress.sin_zero), 8);
  302. connecttimeout = FALSE;
  303. signal(SIGALRM, connectalarmhandler);
  304. alarm(90);
  305. ret = connect(sockethandle, (struct sockaddr *)&cvaddress, sizeof(struct sockaddr));
  306. if (ret == -1 && errno != EINPROGRESS)
  307. {
  308. if (!connecttimeout)
  309. {
  310. msg = g_string_new(strerror(errno));
  311. updatestatusbar(msg);
  312. g_string_free(msg, TRUE);
  313. }
  314. return;
  315. }
  316. /* connection is non-blocking, so we set up a monitor to see if connection is succesful */
  317. connectmonitor = gdk_input_add(sockethandle, GDK_INPUT_WRITE, GTK_SIGNAL_FUNC(socket_connected), NULL);
  318. }
  319. /*
  320. * Disconnect routine. The socket and logfile are closed, the lockfile for
  321. * logging is removed, statusbar updated and hash tabled freed.
  322. * Also, menu items are adjusted, so only 'connect' will be active.
  323. */
  324. void cvdisconnect(void)
  325. {
  326. GString *msg = g_string_new("");
  327. GtkWidget *menuopen, *menuclose;
  328. close(sockethandle);
  329. if (preferences.logging) fclose(logfp);
  330. if (rxmonitor) gdk_input_remove(rxmonitor);
  331. g_string_sprintf(msg, _("Connection closed"));
  332. updatestatusbar(msg);
  333. g_string_free(msg, TRUE);
  334. menuclose = lookup_widget(mainwindow, "close");
  335. menuopen = lookup_widget(mainwindow, "open");
  336. gtk_widget_set_sensitive(menuopen, 1);
  337. gtk_widget_set_sensitive(menuclose, 0);
  338. if (callsigns)
  339. {
  340. g_hash_table_foreach_remove(callsigns, (GHRFunc)remove_calls, NULL);
  341. g_hash_table_destroy(callsigns);
  342. }
  343. usercolor = 0;
  344. connected = FALSE;
  345. }
  346. /*
  347. * A message is received here. We create a buffer where the incoming message is
  348. * stored. Then we check for possible problems (displayed in the statusbar). If
  349. * all is OK, the message gets displayed and is written to the log if this is
  350. * opened.
  351. */
  352. void rx(gpointer data, gint source, GdkInputCondition condition)
  353. {
  354. gchar cvbuf[8192];
  355. gint numbytes;
  356. GString *msg = g_string_new("");
  357. memset(cvbuf, 0, 8192*sizeof(gchar));
  358. numbytes = read(sockethandle, cvbuf, 4096);
  359. if (numbytes == - 1 ) /* an error has occured */
  360. {
  361. msg = g_string_new(strerror(errno));
  362. updatestatusbar(msg);
  363. g_string_free(msg, TRUE);
  364. cvdisconnect();
  365. return;
  366. }
  367. if ( numbytes == 0 ) /* remote end has closed connection */
  368. {
  369. cvdisconnect();
  370. return;
  371. }
  372. maintext_add(cvbuf, MESSAGE_RX);
  373. if (preferences.logging)
  374. {
  375. fprintf(logfp, "%s", cvbuf);
  376. fflush(logfp);
  377. }
  378. }
  379. /*
  380. * Save messages to history, send out to the socket, echo to the main window,
  381. * and write to logfile.
  382. */
  383. void tx(GString *message)
  384. {
  385. tx_save(message);
  386. message = g_string_append(message, "\n");
  387. write(sockethandle, message->str, message->len);
  388. maintext_add(message->str, MESSAGE_TX);
  389. if (preferences.logging && connected)
  390. {
  391. fprintf(logfp, "%s", message->str);
  392. fflush(logfp);
  393. }
  394. }