2 #include "rudp_punch.h"
6 * Local port the socket will bind to(but not listen on).
7 * \param listening_peer
8 * The address the other side listening on, may be NULL.
9 * \param candidate_peers, n_peer
10 * The possible addresses the other side can be reached by.
12 RUDPSOCKET RUDPPunch(unsigned short local_port,
13 const struct sockaddr *listening_peer,
14 const struct sockaddr_in *candidate_peers, int n_peer,
15 CHECKCONNECTIONCB cb, void *cb_data)
17 #define MAX_CANDIDATES 10
18 RUDPSOCKET rsock_c = INVALID_RUDPSOCKET, rsocks[MAX_CANDIDATES];
19 RUDPSOCKCHNO rset[MAX_CANDIDATES], wset[MAX_CANDIDATES], eset[MAX_CANDIDATES];
20 int n_rset, n_wset, n_eset, nr, nw, ne;
22 struct sockaddr_in sai;
25 if(n_peer > 9) n_peer = 9;
26 if(listening_peer) n_peer++;
28 memset(&sai, 0, sizeof(sai));
29 sai.sin_family = AF_INET;
30 sai.sin_port = htons(local_port);
32 n_rset = n_wset = n_eset = 0;
33 for(i=0; i<n_peer; i++)
35 rsocks[i] = RUDPSocket();
36 RUDPSetSockOpt(rsocks[i], OPT_REUSEADDR, &opt, sizeof(int));
37 if(RUDPBind(rsocks[i], (struct sockaddr*)&sai, sizeof(sai)) != 0)
39 dbg_msg("RUDPBind failed.\n");
41 RUDPSetSockOpt(rsocks[i], OPT_NBLK, &opt, sizeof(int));
42 if(i==n_peer-1 && listening_peer)
44 RUDPConnect(rsocks[i], listening_peer, sizeof(struct sockaddr));
48 RUDPConnect(rsocks[i], (const struct sockaddr*)&candidate_peers[i], sizeof(struct sockaddr));
50 RUDP_SET(rsocks[i], -1, eset, n_eset);
51 RUDP_SET(rsocks[i], -1, rset, n_rset);
55 tv.tv_sec = 1; tv.tv_usec = 0;
56 time_t t0 = time(NULL);
59 nr = n_rset; nw = n_wset; ne = n_eset;
60 if(RUDPSelect(rset, &nr, wset, &nw, eset, &ne, &tv) <= 0)
63 for(i=0; i<n_peer; i++)
64 if(rsocks[i] != INVALID_RUDPSOCKET && RUDP_ISSET(rsocks[i], rset, nr)) //data arrived
66 switch(cb(rsocks[i], CONNSTATUS_READABLE, cb_data))
68 case CHECKCONNECTION_OK:
70 rsocks[i] = INVALID_RUDPSOCKET;
73 case CHECKCONNECTION_FAKE:
75 rsocks[i] = INVALID_RUDPSOCKET;
80 for(i=0; i<n_peer; i++)
81 if(rsocks[i] != INVALID_RUDPSOCKET && RUDP_ISSET(rsocks[i], wset, nw)) //connected
83 switch(cb(rsocks[i], (i==n_peer-1)&&listening_peer?CONNSTATUS_ACCEPTED:CONNSTATUS_CONNECTED, cb_data))
85 case CHECKCONNECTION_OK:
87 rsocks[i] = INVALID_RUDPSOCKET;
90 case CHECKCONNECTION_CONTINUE:
91 RUDP_SET(rsocks[i], 0, rset, n_rset);
92 RUDP_CLR(rsocks[i], 0, wset, n_wset);
95 case CHECKCONNECTION_FAKE:
97 rsocks[i] = INVALID_RUDPSOCKET;
102 for(i=0; i<n_peer; i++)
103 if(rsocks[i] != INVALID_RUDPSOCKET && RUDP_ISSET(rsocks[i], eset, ne))
105 if(i==n_peer-1 && listening_peer)
107 RUDPConnect(rsocks[i], listening_peer, sizeof(struct sockaddr));
110 RUDPConnect(rsocks[i], (const struct sockaddr*)&candidate_peers[i], sizeof(struct sockaddr));
113 } while(time(NULL) - t0 < 8);
116 for(i=0; i<n_peer; i++)
117 if(rsocks[i] != INVALID_RUDPSOCKET)
118 RUDPClose(rsocks[i]);
122 int _ClientPunchCb(RUDPSOCKET s, int status, void *data)
129 case CONNSTATUS_CONNECTED:
130 case CONNSTATUS_ACCEPTED:
131 if(RUDPSend(s, (char*)data, sizeof(struct dcs_header) + ntohl(((struct dcs_header*)data)->length), 0) < 0)
133 return CHECKCONNECTION_CONTINUE;
135 case CONNSTATUS_READABLE:
136 len = RUDPRecv(s, &chno, &dp, sizeof(dp));
137 if(len >= sizeof(struct dcs_header) && check_dcs_header(&dp.dh) && dp.dh.cls == CLS_RESPONSE && dp.dh.st == ST_IPCAM)
138 return(dp.dh.status == 0)?CHECKCONNECTION_OK:-dp.dh.status;
144 return CHECKCONNECTION_CONTINUE;
147 int _CameraPunchCb(RUDPSOCKET s, int status, void *data)
153 case CONNSTATUS_CONNECTED:
154 ;//if this is client, send confirmation
156 case CONNSTATUS_ACCEPTED:
159 case CONNSTATUS_READABLE:
160 len = RUDPRecv(s, &chno, &dp, sizeof(dp));
161 if(len >= sizeof(struct dcs_header) && check_dcs_header(&dp.dh) && dp.dh.cls == CLS_REQUEST && dp.dh.st == ST_CLT)
163 ;//check session and password
168 return CHECKCONNECTION_CONTINUE;