diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index c8fcf760..89d65ed6 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -390,6 +390,9 @@ const struct prefs vars[] = {"dcc_permissions", P_OFFINT (hex_dcc_permissions), TYPE_INT}, {"dcc_port_first", P_OFFINT (hex_dcc_port_first), TYPE_INT}, {"dcc_port_last", P_OFFINT (hex_dcc_port_last), TYPE_INT}, + {"dcc_nat_lease", P_OFFINT (hex_dcc_nat_lease), TYPE_INT}, + {"dcc_nat_map", P_OFFINT (hex_dcc_nat_map), TYPE_BOOL}, + {"dcc_passive_prefer", P_OFFINT (hex_dcc_passive_prefer), TYPE_BOOL}, {"dcc_remove", P_OFFINT (hex_dcc_remove), TYPE_BOOL}, {"dcc_save_nick", P_OFFINT (hex_dcc_save_nick), TYPE_BOOL}, {"dcc_send_fillspaces", P_OFFINT (hex_dcc_send_fillspaces), TYPE_BOOL}, @@ -762,6 +765,7 @@ load_default_config(void) prefs.hex_away_show_once = 1; prefs.hex_away_track = 1; prefs.hex_dcc_auto_resume = 1; + prefs.hex_dcc_passive_prefer = 1; #ifndef WIN32 prefs.hex_dcc_fast_send = 1; #endif @@ -835,6 +839,7 @@ load_default_config(void) prefs.hex_dcc_permissions = 0600; prefs.hex_dcc_stall_timeout = 60; prefs.hex_dcc_timeout = 180; + prefs.hex_dcc_nat_lease = 1800; prefs.hex_flood_ctcp_num = 5; prefs.hex_flood_ctcp_time = 30; prefs.hex_flood_msg_num = 5; diff --git a/src/common/dcc.c b/src/common/dcc.c index a8fb68fa..8d5b7c9a 100644 --- a/src/common/dcc.c +++ b/src/common/dcc.c @@ -87,6 +87,41 @@ static gboolean dcc_read (GIOChannel *, GIOCondition, struct DCC *); static gboolean dcc_read_ack (GIOChannel *source, GIOCondition condition, struct DCC *dcc); static int dcc_check_timeouts (void); + +static int +nat_map_run (int add, int port, int proto, int lease) +{ + char cmd[512]; + int status = 0; + if (add) + g_snprintf (cmd, sizeof (cmd), "upnpc -a 127.0.0.1 %d %d TCP %d >/dev/null 2>&1", port, port, lease); + else + g_snprintf (cmd, sizeof (cmd), "upnpc -d %d TCP >/dev/null 2>&1", port); + if (!g_spawn_command_line_sync (cmd, NULL, NULL, &status, NULL)) + return FALSE; + return status == 0; +} + +static void +dcc_nat_unmap (struct DCC *dcc) +{ + if (!dcc->map_port) + return; + nat_map_run (0, dcc->map_port, dcc->type, 0); + dcc->map_port = 0; + dcc->map_next_refresh = 0; +} + +static void +dcc_nat_refresh (struct DCC *dcc) +{ + if (!dcc->map_port || prefs.hex_dcc_nat_lease <= 0) + return; + if (time (0) < dcc->map_next_refresh) + return; + if (nat_map_run (1, dcc->map_port, dcc->type, prefs.hex_dcc_nat_lease)) + dcc->map_next_refresh = time (0) + prefs.hex_dcc_nat_lease / 2; +} static int new_id(void) { static int id = 0; @@ -254,7 +289,8 @@ dcc_check_timeouts (void) switch (dcc->dccstat) { case STAT_ACTIVE: - dcc_calc_cps (dcc); + dcc_calc_cps (dcc); + dcc_nat_refresh (dcc); fe_dcc_update (dcc); if (dcc->type == TYPE_SEND || dcc->type == TYPE_RECV) @@ -390,6 +426,7 @@ dcc_close (struct DCC *dcc, enum dcc_state dccstat, int destroy) } dcc_remove_from_sum (dcc); + dcc_nat_unmap (dcc); if (dcc->fp != -1) { @@ -1698,6 +1735,11 @@ dcc_listen_init (struct DCC *dcc, session *sess) getsockname (dcc->sok, (struct sockaddr *) &SAddr, &len); dcc->port = ntohs (SAddr.sin_port); + if (prefs.hex_dcc_nat_map && nat_map_run (1, dcc->port, dcc->type, prefs.hex_dcc_nat_lease)) + { + dcc->map_port = dcc->port; + dcc->map_next_refresh = time (0) + prefs.hex_dcc_nat_lease / 2; + } dcc->addr = dcc_get_my_address (sess); @@ -1846,8 +1888,19 @@ dcc_send (struct session *sess, char *to, char *filename, gint64 maxcps, int pas return; } - if (passive || dcc_listen_init (dcc, sess)) + if ((passive || prefs.hex_dcc_passive_prefer) || dcc_listen_init (dcc, sess)) { + if (passive || prefs.hex_dcc_passive_prefer) + { + guint32 offer_addr = dcc_get_my_address (sess); + struct sockaddr_in saddr; + socklen_t slen = sizeof (saddr); + memset (&saddr, 0, sizeof (saddr)); + getsockname (dcc->serv->sok, (struct sockaddr *) &saddr, &slen); + if (offer_addr == 0) + offer_addr = prefs.local_ip != 0xffffffff ? prefs.local_ip : saddr.sin_addr.s_addr; + dcc->addr = ntohl (offer_addr); + } char havespaces = 0; while (*filename) { @@ -1868,13 +1921,13 @@ dcc_send (struct session *sess, char *to, char *filename, gint64 maxcps, int pas } else fe_dcc_add (dcc); - if (passive) + if (passive || prefs.hex_dcc_passive_prefer) { dcc->pasvid = new_id(); g_snprintf (outbuf, sizeof (outbuf), (havespaces) ? - "DCC SEND \"%s\" 199 0 %" G_GUINT64_FORMAT " %d" : - "DCC SEND %s 199 0 %" G_GUINT64_FORMAT " %d", - file_part (dcc->file), + "DCC SEND \"%s\" %u 0 %" G_GUINT64_FORMAT " %d" : + "DCC SEND %s %u 0 %" G_GUINT64_FORMAT " %d", + file_part (dcc->file), dcc->addr, dcc->size, dcc->pasvid); } else @@ -2292,8 +2345,19 @@ dcc_chat (struct session *sess, char *nick, int passive) dcc->dccstat = STAT_QUEUED; dcc->type = TYPE_CHATSEND; dcc->nick = g_strdup (nick); - if (passive || dcc_listen_init (dcc, sess)) + if ((passive || prefs.hex_dcc_passive_prefer) || dcc_listen_init (dcc, sess)) { + if (passive || prefs.hex_dcc_passive_prefer) + { + guint32 offer_addr = dcc_get_my_address (sess); + struct sockaddr_in saddr; + socklen_t slen = sizeof (saddr); + memset (&saddr, 0, sizeof (saddr)); + getsockname (dcc->serv->sok, (struct sockaddr *) &saddr, &slen); + if (offer_addr == 0) + offer_addr = prefs.local_ip != 0xffffffff ? prefs.local_ip : saddr.sin_addr.s_addr; + dcc->addr = ntohl (offer_addr); + } if (prefs.hex_gui_autoopen_chat) { if (fe_dcc_open_chat_win (TRUE)) /* already open? add only */ @@ -2301,11 +2365,11 @@ dcc_chat (struct session *sess, char *nick, int passive) } else fe_dcc_add (dcc); - if (passive) + if (passive || prefs.hex_dcc_passive_prefer) { dcc->pasvid = new_id (); - g_snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat 199 %d %d", - dcc->port, dcc->pasvid); + g_snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat %u 0 %d", + dcc->addr, dcc->pasvid); } else { g_snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat %u %d", diff --git a/src/common/dcc.h b/src/common/dcc.h index c28f04d3..d696cde7 100644 --- a/src/common/dcc.h +++ b/src/common/dcc.h @@ -55,6 +55,9 @@ struct DCC int wiotag; /* writing/sending io tag */ int port; int pasvid; /* mIRC's passive DCC id */ + int map_port; + int map_proto; + int map_next_refresh; gint64 cps; int resume_error; int resume_errno; diff --git a/src/common/zoitechat.h b/src/common/zoitechat.h index 6f8591c3..3811d7f7 100644 --- a/src/common/zoitechat.h +++ b/src/common/zoitechat.h @@ -117,6 +117,8 @@ struct zoitechatprefs unsigned int hex_dcc_remove; unsigned int hex_dcc_save_nick; unsigned int hex_dcc_send_fillspaces; + unsigned int hex_dcc_passive_prefer; + unsigned int hex_dcc_nat_map; unsigned int hex_gui_autoopen_chat; unsigned int hex_gui_autoopen_dialog; unsigned int hex_gui_autoopen_recv; @@ -241,6 +243,7 @@ struct zoitechatprefs int hex_dcc_permissions; int hex_dcc_port_first; int hex_dcc_port_last; + int hex_dcc_nat_lease; int hex_dcc_stall_timeout; int hex_dcc_timeout; int hex_flood_ctcp_num; /* flood */ diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c index d2dc0882..16221b7b 100644 --- a/src/fe-gtk/setup.c +++ b/src/fe-gtk/setup.c @@ -650,6 +650,9 @@ static const setting network_settings[] = {ST_NUMBER, N_("First DCC listen port:"), P_OFFINTNL(hex_dcc_port_first), 0, 0, 65535}, {ST_NUMBER, N_("Last DCC listen port:"), P_OFFINTNL(hex_dcc_port_last), 0, (const char **)N_("!Leave ports at zero for full range."), 65535}, + {ST_TOGGLE, N_("Prefer passive DCC"), P_OFFINTNL(hex_dcc_passive_prefer), 0, 0, 0}, + {ST_TOGGLE, N_("Try UPnP/NAT-PMP/PCP mapping"), P_OFFINTNL(hex_dcc_nat_map), 0, 0, 0}, + {ST_NUMBER, N_("NAT mapping lease (sec):"), P_OFFINTNL(hex_dcc_nat_lease), 0, 0, 86400}, {ST_HEADER, N_("Proxy Server"), 0, 0, 0, 0}, {ST_ENTRY, N_("Hostname:"), P_OFFSETNL(hex_net_proxy_host), 0, 0, sizeof prefs.hex_net_proxy_host},