Blame view

3rdparty/libmodbus/modbus-tcp.c 19.5 KB
783ce3c5   Peter M. Groen   Setting up workin...
1
  
500c015a   Peter M. Groen   Setting up workin...
2
3
4
5
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <errno.h>
500c015a   Peter M. Groen   Setting up workin...
6
  #include <unistd.h>
500c015a   Peter M. Groen   Setting up workin...
7
8
9
  #include <signal.h>
  #include <sys/types.h>
  
500c015a   Peter M. Groen   Setting up workin...
10
11
12
13
14
15
16
17
18
19
20
21
22
  # include <sys/socket.h>
  # include <sys/ioctl.h>
  
  #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
  # define OS_BSD
  # include <netinet/in_systm.h>
  #endif
  
  # include <netinet/in.h>
  # include <netinet/ip.h>
  # include <netinet/tcp.h>
  # include <arpa/inet.h>
  # include <netdb.h>
46785270   Peter M. Groen   Setting up workin...
23
  
500c015a   Peter M. Groen   Setting up workin...
24
25
26
27
  #if !defined(MSG_NOSIGNAL)
  #define MSG_NOSIGNAL 0
  #endif
  
500c015a   Peter M. Groen   Setting up workin...
28
29
30
31
32
  #include "modbus-private.h"
  
  #include "modbus-tcp.h"
  #include "modbus-tcp-private.h"
  
500c015a   Peter M. Groen   Setting up workin...
33
34
35
  static int _modbus_set_slave(modbus_t *ctx, int slave)
  {
      /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
783ce3c5   Peter M. Groen   Setting up workin...
36
37
      if ( slave >= 0 && slave <= 247) 
      {
500c015a   Peter M. Groen   Setting up workin...
38
          ctx->slave = slave;
783ce3c5   Peter M. Groen   Setting up workin...
39
40
41
      } 
      else if ( slave == MODBUS_TCP_SLAVE ) 
      {
500c015a   Peter M. Groen   Setting up workin...
42
43
44
          /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
           * restore the default value. */
          ctx->slave = slave;
783ce3c5   Peter M. Groen   Setting up workin...
45
46
47
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
48
49
50
51
52
53
54
55
          errno = EINVAL;
          return -1;
      }
  
      return 0;
  }
  
  /* Builds a TCP request header */
783ce3c5   Peter M. Groen   Setting up workin...
56
  static int _modbus_tcp_build_request_basis( modbus_t *ctx, int function, int addr, int nb, uint8_t *req )
500c015a   Peter M. Groen   Setting up workin...
57
58
59
60
61
62
63
64
  {
      modbus_tcp_t *ctx_tcp = ctx->backend_data;
  
      /* Increase transaction ID */
      if (ctx_tcp->t_id < UINT16_MAX)
          ctx_tcp->t_id++;
      else
          ctx_tcp->t_id = 0;
783ce3c5   Peter M. Groen   Setting up workin...
65
  
500c015a   Peter M. Groen   Setting up workin...
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
      req[0] = ctx_tcp->t_id >> 8;
      req[1] = ctx_tcp->t_id & 0x00ff;
  
      /* Protocol Modbus */
      req[2] = 0;
      req[3] = 0;
  
      /* Length will be defined later by set_req_length_tcp at offsets 4
         and 5 */
  
      req[6] = ctx->slave;
      req[7] = function;
      req[8] = addr >> 8;
      req[9] = addr & 0x00ff;
      req[10] = nb >> 8;
      req[11] = nb & 0x00ff;
  
      return _MODBUS_TCP_PRESET_REQ_LENGTH;
  }
  
  /* Builds a TCP response header */
783ce3c5   Peter M. Groen   Setting up workin...
87
  static int _modbus_tcp_build_response_basis( sft_t *sft, uint8_t *rsp )
500c015a   Peter M. Groen   Setting up workin...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  {
      /* Extract from MODBUS Messaging on TCP/IP Implementation
         Guide V1.0b (page 23/46):
         The transaction identifier is used to associate the future
         response with the request. */
      rsp[0] = sft->t_id >> 8;
      rsp[1] = sft->t_id & 0x00ff;
  
      /* Protocol Modbus */
      rsp[2] = 0;
      rsp[3] = 0;
  
      /* Length will be set later by send_msg (4 and 5) */
  
      /* The slave ID is copied from the indication */
      rsp[6] = sft->slave;
      rsp[7] = sft->function;
  
      return _MODBUS_TCP_PRESET_RSP_LENGTH;
  }
  
  
783ce3c5   Peter M. Groen   Setting up workin...
110
  static int _modbus_tcp_prepare_response_tid( const uint8_t *req, int *req_length )
500c015a   Peter M. Groen   Setting up workin...
111
112
113
114
  {
      return (req[0] << 8) + req[1];
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
115
  static int _modbus_tcp_send_msg_pre( uint8_t *req, int req_length )
500c015a   Peter M. Groen   Setting up workin...
116
117
118
119
120
121
122
123
124
125
  {
      /* Substract the header length to the message length */
      int mbap_length = req_length - 6;
  
      req[4] = mbap_length >> 8;
      req[5] = mbap_length & 0x00FF;
  
      return req_length;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
126
  static ssize_t _modbus_tcp_send( modbus_t *ctx, const uint8_t *req, int req_length )
500c015a   Peter M. Groen   Setting up workin...
127
128
129
130
131
132
133
134
  {
      /* MSG_NOSIGNAL
         Requests not to send SIGPIPE on errors on stream oriented
         sockets when the other end breaks the connection.  The EPIPE
         error is still returned. */
      return send(ctx->s, (const char *)req, req_length, MSG_NOSIGNAL);
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
135
136
  static int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) 
  {
500c015a   Peter M. Groen   Setting up workin...
137
138
139
      return _modbus_receive_msg(ctx, req, MSG_INDICATION);
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
140
141
142
  static ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) 
  {
      return recv( ctx->s, (char *)rsp, rsp_length, 0 );
500c015a   Peter M. Groen   Setting up workin...
143
144
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
145
  static int _modbus_tcp_check_integrity( modbus_t *ctx, uint8_t *msg, const int msg_length )
500c015a   Peter M. Groen   Setting up workin...
146
147
148
149
  {
      return msg_length;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
150
  static int _modbus_tcp_pre_check_confirmation( modbus_t *ctx, const uint8_t *req, const uint8_t *rsp, int rsp_length )
500c015a   Peter M. Groen   Setting up workin...
151
152
153
  {
      /* Check transaction ID */
      if (req[0] != rsp[0] || req[1] != rsp[1]) {
783ce3c5   Peter M. Groen   Setting up workin...
154
155
          if (ctx->debug) 
          {
500c015a   Peter M. Groen   Setting up workin...
156
157
158
159
160
161
162
163
              fprintf(stderr, "Invalid transaction ID received 0x%X (not 0x%X)\n",
                      (rsp[0] << 8) + rsp[1], (req[0] << 8) + req[1]);
          }
          errno = EMBBADDATA;
          return -1;
      }
  
      /* Check protocol ID */
783ce3c5   Peter M. Groen   Setting up workin...
164
165
166
167
      if (rsp[2] != 0x0 && rsp[3] != 0x0) 
      {
          if (ctx->debug) 
          {
500c015a   Peter M. Groen   Setting up workin...
168
169
170
171
172
173
174
175
176
177
              fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n",
                      (rsp[2] << 8) + rsp[3]);
          }
          errno = EMBBADDATA;
          return -1;
      }
  
      return 0;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
178
  static int _modbus_tcp_set_ipv4_options( int s )
500c015a   Peter M. Groen   Setting up workin...
179
180
181
182
183
184
185
  {
      int rc;
      int option;
  
      /* Set the TCP no delay flag */
      /* SOL_TCP = IPPROTO_TCP */
      option = 1;
783ce3c5   Peter M. Groen   Setting up workin...
186
187
188
      rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *)&option, sizeof(int) );
      if (rc == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
189
190
191
192
193
194
195
          return -1;
      }
  
      /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
       * make sockets non-blocking */
      /* Do not care about the return value, this is optional */
  #if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
500c015a   Peter M. Groen   Setting up workin...
196
197
198
      option = 1;
      ioctl(s, FIONBIO, &option);
  #endif
500c015a   Peter M. Groen   Setting up workin...
199
  
500c015a   Peter M. Groen   Setting up workin...
200
201
202
203
204
205
206
207
208
209
210
      /**
       * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
       * necessary to workaround that problem.
       **/
      /* Set the IP low delay option */
      option = IPTOS_LOWDELAY;
      rc = setsockopt(s, IPPROTO_IP, IP_TOS,
                      (const void *)&option, sizeof(int));
      if (rc == -1) {
          return -1;
      }
500c015a   Peter M. Groen   Setting up workin...
211
212
213
214
  
      return 0;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
215
  static int _connect( int sockfd, const struct sockaddr *addr, socklen_t addrlen, const struct timeval *ro_tv )
500c015a   Peter M. Groen   Setting up workin...
216
217
218
  {
      int rc = connect(sockfd, addr, addrlen);
  
783ce3c5   Peter M. Groen   Setting up workin...
219
220
      if (rc == -1 && errno == EINPROGRESS) 
      {
500c015a   Peter M. Groen   Setting up workin...
221
222
223
224
225
226
227
228
229
          fd_set wset;
          int optval;
          socklen_t optlen = sizeof(optval);
          struct timeval tv = *ro_tv;
  
          /* Wait to be available in writing */
          FD_ZERO(&wset);
          FD_SET(sockfd, &wset);
          rc = select(sockfd + 1, NULL, &wset, NULL, &tv);
783ce3c5   Peter M. Groen   Setting up workin...
230
231
          if (rc <= 0) 
          {
500c015a   Peter M. Groen   Setting up workin...
232
233
234
235
236
237
              /* Timeout or fail */
              return -1;
          }
  
          /* The connection is established if SO_ERROR and optval are set to 0 */
          rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen);
783ce3c5   Peter M. Groen   Setting up workin...
238
239
          if ( rc == 0 && optval == 0 ) 
          {
500c015a   Peter M. Groen   Setting up workin...
240
              return 0;
783ce3c5   Peter M. Groen   Setting up workin...
241
242
243
          } 
          else 
          {
500c015a   Peter M. Groen   Setting up workin...
244
245
246
247
248
249
250
251
              errno = ECONNREFUSED;
              return -1;
          }
      }
      return rc;
  }
  
  /* Establishes a modbus TCP connection with a Modbus server. */
783ce3c5   Peter M. Groen   Setting up workin...
252
  static int _modbus_tcp_connect( modbus_t *ctx )
500c015a   Peter M. Groen   Setting up workin...
253
254
255
256
257
258
259
  {
      int rc;
      /* Specialized version of sockaddr for Internet socket address (same size) */
      struct sockaddr_in addr;
      modbus_tcp_t *ctx_tcp = ctx->backend_data;
      int flags = SOCK_STREAM;
  
500c015a   Peter M. Groen   Setting up workin...
260
261
262
263
264
265
266
267
268
  #ifdef SOCK_CLOEXEC
      flags |= SOCK_CLOEXEC;
  #endif
  
  #ifdef SOCK_NONBLOCK
      flags |= SOCK_NONBLOCK;
  #endif
  
      ctx->s = socket(PF_INET, flags, 0);
783ce3c5   Peter M. Groen   Setting up workin...
269
270
      if (ctx->s == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
271
272
273
274
          return -1;
      }
  
      rc = _modbus_tcp_set_ipv4_options(ctx->s);
783ce3c5   Peter M. Groen   Setting up workin...
275
276
      if (rc == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
277
278
279
280
281
          close(ctx->s);
          ctx->s = -1;
          return -1;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
282
283
      if (ctx->debug) 
      {
500c015a   Peter M. Groen   Setting up workin...
284
285
286
287
288
289
290
          printf("Connecting to %s:%d\n", ctx_tcp->ip, ctx_tcp->port);
      }
  
      addr.sin_family = AF_INET;
      addr.sin_port = htons(ctx_tcp->port);
      addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
      rc = _connect(ctx->s, (struct sockaddr *)&addr, sizeof(addr), &ctx->response_timeout);
783ce3c5   Peter M. Groen   Setting up workin...
291
292
293
      if( rc == -1 ) 
      {
          close( ctx->s );
500c015a   Peter M. Groen   Setting up workin...
294
295
296
297
298
299
300
301
          ctx->s = -1;
          return -1;
      }
  
      return 0;
  }
  
  /* Establishes a modbus TCP PI connection with a Modbus server. */
783ce3c5   Peter M. Groen   Setting up workin...
302
  static int _modbus_tcp_pi_connect( modbus_t *ctx )
500c015a   Peter M. Groen   Setting up workin...
303
304
305
306
307
308
309
  {
      int rc;
      struct addrinfo *ai_list;
      struct addrinfo *ai_ptr;
      struct addrinfo ai_hints;
      modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data;
  
500c015a   Peter M. Groen   Setting up workin...
310
311
312
313
314
315
316
317
318
319
320
      memset(&ai_hints, 0, sizeof(ai_hints));
  #ifdef AI_ADDRCONFIG
      ai_hints.ai_flags |= AI_ADDRCONFIG;
  #endif
      ai_hints.ai_family = AF_UNSPEC;
      ai_hints.ai_socktype = SOCK_STREAM;
      ai_hints.ai_addr = NULL;
      ai_hints.ai_canonname = NULL;
      ai_hints.ai_next = NULL;
  
      ai_list = NULL;
783ce3c5   Peter M. Groen   Setting up workin...
321
322
323
324
325
      rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service, &ai_hints, &ai_list);
      if (rc != 0) 
      {
          if (ctx->debug) 
          {
500c015a   Peter M. Groen   Setting up workin...
326
327
328
329
330
331
              fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc));
          }
          errno = ECONNREFUSED;
          return -1;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
332
333
      for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) 
      {
500c015a   Peter M. Groen   Setting up workin...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
          int flags = ai_ptr->ai_socktype;
          int s;
  
  #ifdef SOCK_CLOEXEC
          flags |= SOCK_CLOEXEC;
  #endif
  
  #ifdef SOCK_NONBLOCK
          flags |= SOCK_NONBLOCK;
  #endif
  
          s = socket(ai_ptr->ai_family, flags, ai_ptr->ai_protocol);
          if (s < 0)
              continue;
  
          if (ai_ptr->ai_family == AF_INET)
              _modbus_tcp_set_ipv4_options(s);
  
783ce3c5   Peter M. Groen   Setting up workin...
352
353
          if (ctx->debug) 
          {
500c015a   Peter M. Groen   Setting up workin...
354
355
356
357
              printf("Connecting to [%s]:%s\n", ctx_tcp_pi->node, ctx_tcp_pi->service);
          }
  
          rc = _connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen, &ctx->response_timeout);
783ce3c5   Peter M. Groen   Setting up workin...
358
359
          if (rc == -1) 
          {
500c015a   Peter M. Groen   Setting up workin...
360
361
362
363
364
365
366
367
368
369
              close(s);
              continue;
          }
  
          ctx->s = s;
          break;
      }
  
      freeaddrinfo(ai_list);
  
783ce3c5   Peter M. Groen   Setting up workin...
370
371
      if (ctx->s < 0) 
      {
500c015a   Peter M. Groen   Setting up workin...
372
373
374
375
376
377
378
          return -1;
      }
  
      return 0;
  }
  
  /* Closes the network connection and socket in TCP mode */
783ce3c5   Peter M. Groen   Setting up workin...
379
  static void _modbus_tcp_close( modbus_t *ctx )
500c015a   Peter M. Groen   Setting up workin...
380
  {
783ce3c5   Peter M. Groen   Setting up workin...
381
382
      if (ctx->s != -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
383
384
385
386
387
388
          shutdown(ctx->s, SHUT_RDWR);
          close(ctx->s);
          ctx->s = -1;
      }
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
389
  static int _modbus_tcp_flush( modbus_t *ctx )
500c015a   Peter M. Groen   Setting up workin...
390
391
392
393
  {
      int rc;
      int rc_sum = 0;
  
783ce3c5   Peter M. Groen   Setting up workin...
394
395
      do 
      {
500c015a   Peter M. Groen   Setting up workin...
396
397
          /* Extract the garbage from the socket */
          char devnull[MODBUS_TCP_MAX_ADU_LENGTH];
500c015a   Peter M. Groen   Setting up workin...
398
  
783ce3c5   Peter M. Groen   Setting up workin...
399
400
401
          rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT);
          if ( rc > 0 ) 
          {
500c015a   Peter M. Groen   Setting up workin...
402
403
              rc_sum += rc;
          }
783ce3c5   Peter M. Groen   Setting up workin...
404
      } while ( rc == MODBUS_TCP_MAX_ADU_LENGTH );
500c015a   Peter M. Groen   Setting up workin...
405
406
407
408
409
  
      return rc_sum;
  }
  
  /* Listens for any request from one or many modbus masters in TCP */
783ce3c5   Peter M. Groen   Setting up workin...
410
  int modbus_tcp_listen( modbus_t *ctx, int nb_connection )
500c015a   Peter M. Groen   Setting up workin...
411
412
413
414
415
416
  {
      int new_s;
      int enable;
      struct sockaddr_in addr;
      modbus_tcp_t *ctx_tcp;
  
783ce3c5   Peter M. Groen   Setting up workin...
417
418
      if (ctx == NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
419
420
421
422
423
424
          errno = EINVAL;
          return -1;
      }
  
      ctx_tcp = ctx->backend_data;
  
500c015a   Peter M. Groen   Setting up workin...
425
      new_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
783ce3c5   Peter M. Groen   Setting up workin...
426
427
      if ( new_s == -1 ) 
      {
500c015a   Peter M. Groen   Setting up workin...
428
429
430
431
          return -1;
      }
  
      enable = 1;
783ce3c5   Peter M. Groen   Setting up workin...
432
433
      if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR, (char *)&enable, sizeof(enable)) == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
434
435
436
437
438
439
          close(new_s);
          return -1;
      }
  
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
783ce3c5   Peter M. Groen   Setting up workin...
440
  
500c015a   Peter M. Groen   Setting up workin...
441
442
      /* If the modbus port is < to 1024, we need the setuid root. */
      addr.sin_port = htons(ctx_tcp->port);
783ce3c5   Peter M. Groen   Setting up workin...
443
444
      if (ctx_tcp->ip[0] == '0') 
      {
500c015a   Peter M. Groen   Setting up workin...
445
446
          /* Listen any addresses */
          addr.sin_addr.s_addr = htonl(INADDR_ANY);
783ce3c5   Peter M. Groen   Setting up workin...
447
448
449
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
450
451
452
          /* Listen only specified IP address */
          addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
      }
783ce3c5   Peter M. Groen   Setting up workin...
453
454
      if (bind( new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
455
456
457
458
          close(new_s);
          return -1;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
459
460
      if (listen(new_s, nb_connection) == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
461
462
463
464
465
466
467
          close(new_s);
          return -1;
      }
  
      return new_s;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
468
  int modbus_tcp_pi_listen( modbus_t *ctx, int nb_connection )
500c015a   Peter M. Groen   Setting up workin...
469
470
471
472
473
474
475
476
477
478
  {
      int rc;
      struct addrinfo *ai_list;
      struct addrinfo *ai_ptr;
      struct addrinfo ai_hints;
      const char *node;
      const char *service;
      int new_s;
      modbus_tcp_pi_t *ctx_tcp_pi;
  
783ce3c5   Peter M. Groen   Setting up workin...
479
480
      if (ctx == NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
481
482
483
484
485
486
          errno = EINVAL;
          return -1;
      }
  
      ctx_tcp_pi = ctx->backend_data;
  
783ce3c5   Peter M. Groen   Setting up workin...
487
488
      if ( ctx_tcp_pi->node[0] == 0) 
      {
500c015a   Peter M. Groen   Setting up workin...
489
          node = NULL; /* == any */
783ce3c5   Peter M. Groen   Setting up workin...
490
491
492
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
493
494
495
          node = ctx_tcp_pi->node;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
496
497
      if ( ctx_tcp_pi->service[0] == 0 ) 
      {
500c015a   Peter M. Groen   Setting up workin...
498
          service = "502";
783ce3c5   Peter M. Groen   Setting up workin...
499
500
501
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
502
503
504
          service = ctx_tcp_pi->service;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
505
      memset( &ai_hints, 0, sizeof (ai_hints) );
500c015a   Peter M. Groen   Setting up workin...
506
507
508
509
510
511
512
513
514
515
516
517
518
      /* If node is not NULL, than the AI_PASSIVE flag is ignored. */
      ai_hints.ai_flags |= AI_PASSIVE;
  #ifdef AI_ADDRCONFIG
      ai_hints.ai_flags |= AI_ADDRCONFIG;
  #endif
      ai_hints.ai_family = AF_UNSPEC;
      ai_hints.ai_socktype = SOCK_STREAM;
      ai_hints.ai_addr = NULL;
      ai_hints.ai_canonname = NULL;
      ai_hints.ai_next = NULL;
  
      ai_list = NULL;
      rc = getaddrinfo(node, service, &ai_hints, &ai_list);
783ce3c5   Peter M. Groen   Setting up workin...
519
520
521
522
      if (rc != 0) 
      {
          if (ctx->debug) 
          {
500c015a   Peter M. Groen   Setting up workin...
523
524
525
526
527
528
529
              fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc));
          }
          errno = ECONNREFUSED;
          return -1;
      }
  
      new_s = -1;
783ce3c5   Peter M. Groen   Setting up workin...
530
531
      for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) 
      {
500c015a   Peter M. Groen   Setting up workin...
532
533
          int s;
  
783ce3c5   Peter M. Groen   Setting up workin...
534
535
536
537
538
          s = socket( ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol );
          if (s < 0) 
          {
              if (ctx->debug) 
              {
500c015a   Peter M. Groen   Setting up workin...
539
540
541
                  perror("socket");
              }
              continue;
783ce3c5   Peter M. Groen   Setting up workin...
542
543
544
          } 
          else 
          {
500c015a   Peter M. Groen   Setting up workin...
545
              int enable = 1;
783ce3c5   Peter M. Groen   Setting up workin...
546
547
548
              rc = setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (void *)&enable, sizeof (enable) );
              if (rc != 0) 
              {
500c015a   Peter M. Groen   Setting up workin...
549
                  close(s);
783ce3c5   Peter M. Groen   Setting up workin...
550
551
                  if (ctx->debug) 
                  {
500c015a   Peter M. Groen   Setting up workin...
552
553
554
555
556
557
558
                      perror("setsockopt");
                  }
                  continue;
              }
          }
  
          rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
783ce3c5   Peter M. Groen   Setting up workin...
559
560
          if (rc != 0) 
          {
500c015a   Peter M. Groen   Setting up workin...
561
              close(s);
783ce3c5   Peter M. Groen   Setting up workin...
562
563
              if (ctx->debug) 
              {
500c015a   Peter M. Groen   Setting up workin...
564
565
566
567
568
569
                  perror("bind");
              }
              continue;
          }
  
          rc = listen(s, nb_connection);
783ce3c5   Peter M. Groen   Setting up workin...
570
571
          if (rc != 0) 
          {
500c015a   Peter M. Groen   Setting up workin...
572
              close(s);
783ce3c5   Peter M. Groen   Setting up workin...
573
574
              if (ctx->debug) 
              {
500c015a   Peter M. Groen   Setting up workin...
575
576
577
578
579
580
581
582
583
584
                  perror("listen");
              }
              continue;
          }
  
          new_s = s;
          break;
      }
      freeaddrinfo(ai_list);
  
783ce3c5   Peter M. Groen   Setting up workin...
585
586
      if (new_s < 0) 
      {
500c015a   Peter M. Groen   Setting up workin...
587
588
589
590
591
592
593
594
595
596
597
          return -1;
      }
  
      return new_s;
  }
  
  int modbus_tcp_accept(modbus_t *ctx, int *s)
  {
      struct sockaddr_in addr;
      socklen_t addrlen;
  
783ce3c5   Peter M. Groen   Setting up workin...
598
599
      if (ctx == NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
600
601
602
603
604
605
606
607
608
609
610
611
          errno = EINVAL;
          return -1;
      }
  
      addrlen = sizeof(addr);
  #ifdef HAVE_ACCEPT4
      /* Inherit socket flags and use accept4 call */
      ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC);
  #else
      ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen);
  #endif
  
783ce3c5   Peter M. Groen   Setting up workin...
612
613
      if (ctx->s == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
614
615
616
617
618
          close(*s);
          *s = -1;
          return -1;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
619
620
      if (ctx->debug) 
      {
500c015a   Peter M. Groen   Setting up workin...
621
622
623
624
625
626
627
          printf("The client connection from %s is accepted\n",
                 inet_ntoa(addr.sin_addr));
      }
  
      return ctx->s;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
628
  int modbus_tcp_pi_accept( modbus_t *ctx, int *s )
500c015a   Peter M. Groen   Setting up workin...
629
630
631
632
  {
      struct sockaddr_storage addr;
      socklen_t addrlen;
  
783ce3c5   Peter M. Groen   Setting up workin...
633
634
      if (ctx == NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
635
636
637
638
639
640
641
642
643
644
645
          errno = EINVAL;
          return -1;
      }
  
      addrlen = sizeof(addr);
  #ifdef HAVE_ACCEPT4
      /* Inherit socket flags and use accept4 call */
      ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC);
  #else
      ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen);
  #endif
783ce3c5   Peter M. Groen   Setting up workin...
646
647
      if (ctx->s == -1) 
      {
500c015a   Peter M. Groen   Setting up workin...
648
649
650
651
          close(*s);
          *s = -1;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
652
653
      if (ctx->debug) 
      {
500c015a   Peter M. Groen   Setting up workin...
654
655
656
657
658
659
660
661
662
          printf("The client connection is accepted.\n");
      }
  
      return ctx->s;
  }
  
  static int _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read)
  {
      int s_rc;
783ce3c5   Peter M. Groen   Setting up workin...
663
664
665
666
667
668
      while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) 
      {
          if (errno == EINTR) 
          {
              if (ctx->debug) 
              {
500c015a   Peter M. Groen   Setting up workin...
669
670
671
672
673
                  fprintf(stderr, "A non blocked signal was caught\n");
              }
              /* Necessary after an error */
              FD_ZERO(rset);
              FD_SET(ctx->s, rset);
783ce3c5   Peter M. Groen   Setting up workin...
674
675
676
          } 
          else 
          {
500c015a   Peter M. Groen   Setting up workin...
677
678
679
680
              return -1;
          }
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
681
682
      if ( s_rc == 0 ) 
      {
500c015a   Peter M. Groen   Setting up workin...
683
684
685
686
687
688
689
          errno = ETIMEDOUT;
          return -1;
      }
  
      return s_rc;
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
690
691
  static void _modbus_tcp_free( modbus_t *ctx ) 
  {
500c015a   Peter M. Groen   Setting up workin...
692
693
694
695
      free(ctx->backend_data);
      free(ctx);
  }
  
783ce3c5   Peter M. Groen   Setting up workin...
696
697
  const modbus_backend_t _modbus_tcp_backend = 
  {
500c015a   Peter M. Groen   Setting up workin...
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
      _MODBUS_BACKEND_TYPE_TCP,
      _MODBUS_TCP_HEADER_LENGTH,
      _MODBUS_TCP_CHECKSUM_LENGTH,
      MODBUS_TCP_MAX_ADU_LENGTH,
      _modbus_set_slave,
      _modbus_tcp_build_request_basis,
      _modbus_tcp_build_response_basis,
      _modbus_tcp_prepare_response_tid,
      _modbus_tcp_send_msg_pre,
      _modbus_tcp_send,
      _modbus_tcp_receive,
      _modbus_tcp_recv,
      _modbus_tcp_check_integrity,
      _modbus_tcp_pre_check_confirmation,
      _modbus_tcp_connect,
      _modbus_tcp_close,
      _modbus_tcp_flush,
      _modbus_tcp_select,
      _modbus_tcp_free
  };
  
  
783ce3c5   Peter M. Groen   Setting up workin...
720
721
  const modbus_backend_t _modbus_tcp_pi_backend = 
  {
500c015a   Peter M. Groen   Setting up workin...
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
      _MODBUS_BACKEND_TYPE_TCP,
      _MODBUS_TCP_HEADER_LENGTH,
      _MODBUS_TCP_CHECKSUM_LENGTH,
      MODBUS_TCP_MAX_ADU_LENGTH,
      _modbus_set_slave,
      _modbus_tcp_build_request_basis,
      _modbus_tcp_build_response_basis,
      _modbus_tcp_prepare_response_tid,
      _modbus_tcp_send_msg_pre,
      _modbus_tcp_send,
      _modbus_tcp_receive,
      _modbus_tcp_recv,
      _modbus_tcp_check_integrity,
      _modbus_tcp_pre_check_confirmation,
      _modbus_tcp_pi_connect,
      _modbus_tcp_close,
      _modbus_tcp_flush,
      _modbus_tcp_select,
      _modbus_tcp_free
  };
  
783ce3c5   Peter M. Groen   Setting up workin...
743
  modbus_t* modbus_new_tcp( const char *ip, int port )
500c015a   Peter M. Groen   Setting up workin...
744
745
746
747
748
749
  {
      modbus_t *ctx;
      modbus_tcp_t *ctx_tcp;
      size_t dest_size;
      size_t ret_size;
  
783ce3c5   Peter M. Groen   Setting up workin...
750
      ctx = (modbus_t *)malloc(sizeof(modbus_t) );
500c015a   Peter M. Groen   Setting up workin...
751
752
753
754
755
756
757
      _modbus_init_common(ctx);
  
      /* Could be changed after to reach a remote serial Modbus device */
      ctx->slave = MODBUS_TCP_SLAVE;
  
      ctx->backend = &_modbus_tcp_backend;
  
783ce3c5   Peter M. Groen   Setting up workin...
758
      ctx->backend_data = ( modbus_tcp_t *)malloc(sizeof(modbus_tcp_t) );
500c015a   Peter M. Groen   Setting up workin...
759
760
      ctx_tcp = (modbus_tcp_t *)ctx->backend_data;
  
783ce3c5   Peter M. Groen   Setting up workin...
761
762
      if (ip != NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
763
764
          dest_size = sizeof(char) * 16;
          ret_size = strlcpy(ctx_tcp->ip, ip, dest_size);
783ce3c5   Peter M. Groen   Setting up workin...
765
766
          if (ret_size == 0) 
          {
500c015a   Peter M. Groen   Setting up workin...
767
768
769
770
771
772
              fprintf(stderr, "The IP string is empty\n");
              modbus_free(ctx);
              errno = EINVAL;
              return NULL;
          }
  
783ce3c5   Peter M. Groen   Setting up workin...
773
774
          if (ret_size >= dest_size) 
          {
500c015a   Peter M. Groen   Setting up workin...
775
776
777
778
779
              fprintf(stderr, "The IP string has been truncated\n");
              modbus_free(ctx);
              errno = EINVAL;
              return NULL;
          }
783ce3c5   Peter M. Groen   Setting up workin...
780
781
782
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
783
784
785
786
787
788
789
790
791
          ctx_tcp->ip[0] = '0';
      }
      ctx_tcp->port = port;
      ctx_tcp->t_id = 0;
  
      return ctx;
  }
  
  
783ce3c5   Peter M. Groen   Setting up workin...
792
  modbus_t* modbus_new_tcp_pi( const char *node, const char *service )
500c015a   Peter M. Groen   Setting up workin...
793
  {
783ce3c5   Peter M. Groen   Setting up workin...
794
      modbus_t        *ctx;
500c015a   Peter M. Groen   Setting up workin...
795
      modbus_tcp_pi_t *ctx_tcp_pi;
783ce3c5   Peter M. Groen   Setting up workin...
796
797
      size_t           dest_size;
      size_t           ret_size;
500c015a   Peter M. Groen   Setting up workin...
798
799
800
801
802
803
804
805
806
807
808
809
  
      ctx = (modbus_t *)malloc(sizeof(modbus_t));
      _modbus_init_common(ctx);
  
      /* Could be changed after to reach a remote serial Modbus device */
      ctx->slave = MODBUS_TCP_SLAVE;
  
      ctx->backend = &_modbus_tcp_pi_backend;
  
      ctx->backend_data = (modbus_tcp_pi_t *)malloc(sizeof(modbus_tcp_pi_t));
      ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data;
  
783ce3c5   Peter M. Groen   Setting up workin...
810
811
      if (node == NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
812
813
          /* The node argument can be empty to indicate any hosts */
          ctx_tcp_pi->node[0] = 0;
783ce3c5   Peter M. Groen   Setting up workin...
814
815
816
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
817
818
          dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH;
          ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size);
783ce3c5   Peter M. Groen   Setting up workin...
819
820
          if (ret_size == 0) 
          {
500c015a   Peter M. Groen   Setting up workin...
821
822
823
824
825
826
              fprintf(stderr, "The node string is empty\n");
              modbus_free(ctx);
              errno = EINVAL;
              return NULL;
          }
  
783ce3c5   Peter M. Groen   Setting up workin...
827
828
          if (ret_size >= dest_size) 
          {
500c015a   Peter M. Groen   Setting up workin...
829
830
831
832
833
834
835
              fprintf(stderr, "The node string has been truncated\n");
              modbus_free(ctx);
              errno = EINVAL;
              return NULL;
          }
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
836
837
      if (service != NULL) 
      {
500c015a   Peter M. Groen   Setting up workin...
838
839
          dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH;
          ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size);
783ce3c5   Peter M. Groen   Setting up workin...
840
841
842
      } 
      else 
      {
500c015a   Peter M. Groen   Setting up workin...
843
844
845
846
          /* Empty service is not allowed, error catched below. */
          ret_size = 0;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
847
848
      if (ret_size == 0) 
      {
500c015a   Peter M. Groen   Setting up workin...
849
850
851
852
853
854
          fprintf(stderr, "The service string is empty\n");
          modbus_free(ctx);
          errno = EINVAL;
          return NULL;
      }
  
783ce3c5   Peter M. Groen   Setting up workin...
855
856
      if (ret_size >= dest_size) 
      {
500c015a   Peter M. Groen   Setting up workin...
857
858
859
860
861
862
863
864
865
866
          fprintf(stderr, "The service string has been truncated\n");
          modbus_free(ctx);
          errno = EINVAL;
          return NULL;
      }
  
      ctx_tcp_pi->t_id = 0;
  
      return ctx;
  }