/** * Remove a mapping created with (mini)upnpc. Calling * this function will give 'upnpc' 1s to remove tha mapping, * so while this function is non-blocking, a task will be * left with the scheduler for up to 1s past this call. * * @param mini the handle */ void GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini) { char pstr[6]; if (NULL != mini->refresh_task) { GNUNET_SCHEDULER_cancel (mini->refresh_task); mini->refresh_task = NULL; } if (NULL != mini->refresh_cmd) { GNUNET_OS_command_stop (mini->refresh_cmd); mini->refresh_cmd = NULL; } if (NULL != mini->map_cmd) { GNUNET_OS_command_stop (mini->map_cmd); mini->map_cmd = NULL; } if (GNUNET_NO == mini->did_map) { GNUNET_free (mini); return; } mini->ac (mini->ac_cls, GNUNET_NO, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); /* Note: oddly enough, deletion uses the external port whereas * addition uses the internal port; this rarely matters since they * often are the same, but it might... */ GNUNET_snprintf (pstr, sizeof (pstr), "%u", (unsigned int) ntohs (mini->current_addr.sin_port)); LOG (GNUNET_ERROR_TYPE_DEBUG, "Unmapping port %u with UPnP\n", ntohs (mini->current_addr.sin_port)); mini->unmap_cmd = GNUNET_OS_command_run (&process_unmap_output, mini, UNMAP_TIMEOUT, "upnpc", "upnpc", "-d", pstr, mini->is_tcp ? "tcp" : "udp", NULL); }
static void exec_cmd_proc (void *cls, const char *line) { struct SysmonProperty *sp = cls; unsigned long long tmp; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line); if (NULL == line) { GNUNET_OS_command_stop (sp->cmd_exec_handle); sp->cmd_exec_handle = NULL; return; } switch (sp->value_type) { case v_numeric: if (1 != sscanf (line, "%llu", &tmp)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Command output was not a numerical value: `%s'\n", line); return; } break; case v_string: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "NOT IMPLEMENTED\n"); break; default: break; } sp->num_val = tmp; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line); put_property (sp); }
/** * Run "upnpc -l" to find out if our mapping changed. * * @param cls the 'struct GNUNET_NAT_MiniHandle' */ static void do_refresh (void *cls) { struct GNUNET_NAT_MiniHandle *mini = cls; int ac; mini->refresh_task = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini); LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to check if our mapping still exists\n"); mini->found = GNUNET_NO; ac = GNUNET_NO; if (NULL != mini->map_cmd) { /* took way too long, abort it! */ GNUNET_OS_command_stop (mini->map_cmd); mini->map_cmd = NULL; ac = GNUNET_YES; } if (NULL != mini->refresh_cmd) { /* took way too long, abort it! */ GNUNET_OS_command_stop (mini->refresh_cmd); mini->refresh_cmd = NULL; ac = GNUNET_YES; } mini->refresh_cmd = GNUNET_OS_command_run (&process_refresh_output, mini, MAP_TIMEOUT, "upnpc", "upnpc", "-l", NULL); if (GNUNET_YES == ac) mini->ac (mini->ac_cls, GNUNET_SYSERR, NULL, 0, GNUNET_NAT_ERROR_UPNPC_TIMEOUT); }
/** * Process output from our 'unmap' command. * * @param cls the `struct GNUNET_NAT_MiniHandle` * @param line line of output, NULL at the end */ static void process_unmap_output (void *cls, const char *line) { struct GNUNET_NAT_MiniHandle *mini = cls; if (NULL == line) { LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n"); GNUNET_OS_command_stop (mini->unmap_cmd); mini->unmap_cmd = NULL; GNUNET_free (mini); return; } /* we don't really care about the output... */ }
static void exec_cmd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct SysmonProperty *sp = cls; GNUNET_assert (NULL != sp->cmd); if (NULL != sp->cmd_exec_handle) { GNUNET_OS_command_stop (sp->cmd_exec_handle); sp->cmd_exec_handle = NULL; GNUNET_break (0); } GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' `%s'\n", sp->desc, sp->cmd, sp->cmd_args); if (NULL == (sp->cmd_exec_handle = GNUNET_OS_command_run (&exec_cmd_proc, sp, GNUNET_TIME_UNIT_SECONDS, sp->cmd, sp->cmd, sp->cmd_args, NULL))) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' failed\n", sp->desc, sp->cmd); }
/** * Process the output from the 'upnpc -r' command. * * @param cls the `struct GNUNET_NAT_MiniHandle` * @param line line of output, NULL at the end */ static void process_map_output (void *cls, const char *line) { struct GNUNET_NAT_MiniHandle *mini = cls; const char *ipaddr; char *ipa; const char *pstr; unsigned int port; if (NULL == line) { GNUNET_OS_command_stop (mini->map_cmd); mini->map_cmd = NULL; if (GNUNET_YES != mini->did_map) mini->ac (mini->ac_cls, GNUNET_SYSERR, NULL, 0, GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED); if (NULL == mini->refresh_task) mini->refresh_task = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini); return; } /* * The upnpc output we're after looks like this: * * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000" */ if ((NULL == (ipaddr = strstr (line, " "))) || (NULL == (pstr = strstr (ipaddr, ":"))) || (1 != SSCANF (pstr + 1, "%u", &port))) { return; /* skip line */ } ipa = GNUNET_strdup (ipaddr + 1); strstr (ipa, ":")[0] = '\0'; if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr)) { GNUNET_free (ipa); return; /* skip line */ } GNUNET_free (ipa); mini->current_addr.sin_port = htons (port); mini->current_addr.sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN mini->current_addr.sin_len = sizeof (struct sockaddr_in); #endif mini->did_map = GNUNET_YES; mini->ac (mini->ac_cls, GNUNET_YES, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); }
/** * Process the output from "upnpc -l" to see if our * external mapping changed. If so, do the notifications. * * @param cls the `struct GNUNET_NAT_MiniHandle` * @param line line of output, NULL at the end */ static void process_refresh_output (void *cls, const char *line) { struct GNUNET_NAT_MiniHandle *mini = cls; char pstr[9]; const char *s; unsigned int nport; struct in_addr exip; if (NULL == line) { GNUNET_OS_command_stop (mini->refresh_cmd); mini->refresh_cmd = NULL; if (GNUNET_NO == mini->found) { /* mapping disappeared, try to re-create */ if (GNUNET_YES == mini->did_map) { mini->ac (mini->ac_cls, GNUNET_NO, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); mini->did_map = GNUNET_NO; } run_upnpc_r (mini); } return; } if (! mini->did_map) return; /* never mapped, won't find our mapping anyway */ /* we're looking for output of the form: * "ExternalIPAddress = 12.134.41.124" */ s = strstr (line, "ExternalIPAddress = "); if (NULL != s) { s += strlen ("ExternalIPAddress = "); if (1 != inet_pton (AF_INET, s, &exip)) return; /* skip */ if (exip.s_addr == mini->current_addr.sin_addr.s_addr) return; /* no change */ /* update mapping */ mini->ac (mini->ac_cls, GNUNET_NO, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); mini->current_addr.sin_addr = exip; mini->ac (mini->ac_cls, GNUNET_YES, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); return; } /* * we're looking for output of the form: * * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''" * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''" * * the pattern we look for is: * * "%s TCP PORT->STRING:OURPORT *" or * "%s UDP PORT->STRING:OURPORT *" */ GNUNET_snprintf (pstr, sizeof (pstr), ":%u ", mini->port); if (NULL == (s = strstr (line, "->"))) return; /* skip */ if (NULL == strstr (s, pstr)) return; /* skip */ if (1 != SSCANF (line, (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" : "%*u UDP %u->%*s:%*u %*s", &nport)) return; /* skip */ mini->found = GNUNET_YES; if (nport == ntohs (mini->current_addr.sin_port)) return; /* no change */ /* external port changed, update mapping */ mini->ac (mini->ac_cls, GNUNET_NO, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); mini->current_addr.sin_port = htons ((uint16_t) nport); mini->ac (mini->ac_cls, GNUNET_YES, (const struct sockaddr *) &mini->current_addr, sizeof (mini->current_addr), GNUNET_NAT_ERROR_SUCCESS); }