2626
2727// Helper function to resolve the port number
2828static int resolve_port (const char * port_str ) {
29- return atoi (port_str );
29+ if (!port_str || !* port_str ) return -1 ;
30+
31+ // Make sure the string is numeric
32+ for (const char * p = port_str ; * p ; ++ p ) {
33+ if (!isdigit ((unsigned char )* p )) return -1 ;
34+ }
35+
36+ int port = atoi (port_str );
37+ if (port <= 0 || port > 65535 ) return -1 ;
38+ return port ;
3039}
3140
3241// Initialize socket (for both client and server)
3342static int init_socket (const char * protocol ) {
34- int sock = -1 ;
35-
36- if (strcmp (protocol , "tcp" ) == 0 ) {
37- sock = socket (AF_INET , SOCK_STREAM , 0 );
38- } else if (strcmp (protocol , "udp" ) == 0 ) {
39- sock = socket (AF_INET , SOCK_DGRAM , 0 );
40- } else if (strcmp (protocol , "raw" ) == 0 ) {
41- sock = socket (AF_INET , SOCK_RAW , IPPROTO_RAW );
42- } else if (strcmp (protocol , "icmp" ) == 0 ) {
43- sock = socket (AF_INET , SOCK_RAW , IPPROTO_ICMP );
44- } else {
45- return -1 ; // Unsupported protocol
43+ if (!protocol ) return -1 ;
44+
45+ struct {
46+ const char * name ;
47+ int type ;
48+ int proto ;
49+ } protocols [] = {
50+ { "tcp" , SOCK_STREAM , 0 },
51+ { "udp" , SOCK_DGRAM , 0 },
52+ { "raw" , SOCK_RAW , IPPROTO_RAW },
53+ { "icmp" , SOCK_RAW , IPPROTO_ICMP },
54+ };
55+
56+ for (size_t i = 0 ; i < sizeof (protocols )/sizeof (protocols [0 ]); ++ i ) {
57+ if (strcmp (protocol , protocols [i ].name ) == 0 ) {
58+ int sock = socket (AF_INET , protocols [i ].type , protocols [i ].proto );
59+ return sock >= 0 ? sock : -1 ;
60+ }
4661 }
4762
48- return sock ;
63+ return -1 ; // Unsupported protocol
4964}
5065
5166// Open a new network stream (TCP/UDP)
@@ -91,11 +106,12 @@ fossil_nstream_t *fossil_nstream_open(const char *protocol, const char *host, co
91106
92107// Send data through the network stream
93108ssize_t fossil_nstream_send (fossil_nstream_t * ns , const void * buf , size_t len ) {
94- if (strcmp ( ns -> protocol , "udp" ) == 0 ) {
95- return sendto ( ns -> socket , buf , len , 0 , ( struct sockaddr * ) & ns -> addr , sizeof ( ns -> addr ));
96- } else if (strcmp (ns -> protocol , "raw" ) == 0 || strcmp (ns -> protocol , "icmp" ) == 0 ) {
109+ if (! ns || ns -> socket < 0 || ! buf || len == 0 ) return -1 ;
110+
111+ if (strcmp ( ns -> protocol , "udp" ) == 0 || strcmp (ns -> protocol , "raw" ) == 0 || strcmp (ns -> protocol , "icmp" ) == 0 ) {
97112 return sendto (ns -> socket , buf , len , 0 , (struct sockaddr * )& ns -> addr , sizeof (ns -> addr ));
98113 }
114+
99115 return send (ns -> socket , buf , len , 0 );
100116}
101117
@@ -127,12 +143,14 @@ fossil_nstream_t *fossil_nstream_accept(fossil_nstream_t *ns) {
127143
128144 if (client_sock < 0 ) return NULL ;
129145
130- fossil_nstream_t * client_ns = malloc ( sizeof (fossil_nstream_t ));
146+ fossil_nstream_t * client_ns = calloc ( 1 , sizeof (fossil_nstream_t )); // safer
131147 if (!client_ns ) return NULL ;
132148
133149 client_ns -> socket = client_sock ;
134150 client_ns -> addr = client_addr ;
135- strcpy (client_ns -> protocol , ns -> protocol ); // Inherit protocol from server
151+ strncpy (client_ns -> protocol , ns -> protocol , sizeof (client_ns -> protocol ) - 1 );
152+ client_ns -> protocol [sizeof (client_ns -> protocol ) - 1 ] = '\0' ;
153+ client_ns -> is_tls = ns -> is_tls ; // inherit TLS flag
136154
137155 return client_ns ;
138156}
@@ -145,18 +163,16 @@ void fossil_nstream_close(fossil_nstream_t *ns) {
145163
146164// Set socket to non-blocking mode
147165int fossil_nstream_set_nonblocking (fossil_nstream_t * ns , int enable ) {
166+ if (!ns || ns -> socket < 0 ) return -1 ;
148167#ifdef _WIN32
149168 u_long mode = enable ? 1 : 0 ;
150169 return ioctlsocket (ns -> socket , FIONBIO , & mode );
151170#else
152171 int flags = fcntl (ns -> socket , F_GETFL , 0 );
153172 if (flags == -1 ) return -1 ;
154173
155- if (enable ) {
156- flags |= O_NONBLOCK ;
157- } else {
158- flags &= ~O_NONBLOCK ;
159- }
174+ if (enable ) flags |= O_NONBLOCK ;
175+ else flags &= ~O_NONBLOCK ;
160176
161177 return fcntl (ns -> socket , F_SETFL , flags );
162178#endif
@@ -190,40 +206,59 @@ int fossil_nstream_wait_writable(fossil_nstream_t *ns, int timeout_ms) {
190206
191207// Connect with timeout
192208int fossil_nstream_connect_timeout (fossil_nstream_t * ns , const char * host , const char * port , int timeout_ms ) {
209+ if (!ns || ns -> socket < 0 || !host || !port ) return -1 ;
210+
193211 struct sockaddr_in server_addr ;
212+ memset (& server_addr , 0 , sizeof (server_addr ));
194213 server_addr .sin_family = AF_INET ;
195214 server_addr .sin_port = htons (resolve_port (port ));
196- server_addr .sin_addr .s_addr = inet_addr (host );
197215
198- // Set socket to non-blocking
199- fossil_nstream_set_nonblocking ( ns , 1 );
216+ in_addr_t addr = inet_addr ( host );
217+ if ( addr == INADDR_NONE ) return -1 ; // Invalid address
200218
201- int result = connect (ns -> socket , (struct sockaddr * )& server_addr , sizeof (server_addr ));
219+ server_addr .sin_addr .s_addr = addr ;
220+
221+ // Set non-blocking
222+ if (fossil_nstream_set_nonblocking (ns , 1 ) != 0 ) return -1 ;
202223
224+ int result = connect (ns -> socket , (struct sockaddr * )& server_addr , sizeof (server_addr ));
203225 if (result < 0 ) {
204226#ifdef _WIN32
205- if (WSAGetLastError () == WSAEWOULDBLOCK ) {
227+ int last_error = WSAGetLastError ();
228+ if (last_error == WSAEWOULDBLOCK ) {
206229#else
207230 if (errno == EINPROGRESS ) {
208231#endif
209- struct timeval timeout ;
210- timeout .tv_sec = timeout_ms / 1000 ;
211- timeout .tv_usec = (timeout_ms % 1000 ) * 1000 ;
212-
213232 fd_set writefds ;
214233 FD_ZERO (& writefds );
215234 FD_SET (ns -> socket , & writefds );
216235
236+ struct timeval timeout ;
237+ timeout .tv_sec = timeout_ms / 1000 ;
238+ timeout .tv_usec = (timeout_ms % 1000 ) * 1000 ;
239+
217240 result = select (ns -> socket + 1 , NULL , & writefds , NULL , & timeout );
218- if (result <= 0 ) return -1 ; // Timeout or error
241+ if (result <= 0 ) {
242+ fossil_nstream_set_nonblocking (ns , 0 );
243+ return -1 ; // Timeout or select error
244+ }
245+
246+ // Check if there was a socket error
247+ int so_error = 0 ;
248+ socklen_t len = sizeof (so_error );
249+ getsockopt (ns -> socket , SOL_SOCKET , SO_ERROR , (char * )& so_error , & len );
250+ if (so_error != 0 ) {
251+ fossil_nstream_set_nonblocking (ns , 0 );
252+ return -1 ;
253+ }
219254 } else {
220- return -1 ; // Immediate error
255+ fossil_nstream_set_nonblocking (ns , 0 );
256+ return -1 ;
221257 }
222258 }
223259
224- // Set socket back to blocking
260+ // Set back to blocking
225261 fossil_nstream_set_nonblocking (ns , 0 );
226-
227262 return 0 ;
228263}
229264
@@ -280,5 +315,11 @@ ssize_t fossil_nstream_ssl_recv(fossil_nstream_t *ns, void *buf, size_t len) {
280315
281316// Get the string representation of the last error
282317const char * fossil_nstream_strerror (void ) {
318+ #ifdef _WIN32
319+ static char buf [64 ];
320+ snprintf (buf , sizeof (buf ), "WSA Error: %d" , WSAGetLastError ());
321+ return buf ;
322+ #else
283323 return strerror (errno );
324+ #endif
284325}
0 commit comments