2 * Copyright © 2001-2013 Stéphane Raimbault <stephane.raimbault@gmail.com>
4 * SPDX-License-Identifier: LGPL-2.1-or-later
9 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
10 * minwg32 headers check WINVER before allowing the use of these */
12 # define WINVER 0x0501
24 #include <sys/types.h>
27 /* Already set in modbus-tcp.h but it seems order matters in VS2005 */
28 # include <winsock2.h>
29 # include <ws2tcpip.h>
31 # define close closesocket
33 # include <sys/socket.h>
34 # include <sys/ioctl.h>
36 #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
38 # include <netinet/in_systm.h>
41 # include <netinet/in.h>
42 # include <netinet/ip.h>
43 # include <netinet/tcp.h>
44 # include <arpa/inet.h>
48 #if !defined(MSG_NOSIGNAL)
49 #define MSG_NOSIGNAL 0
52 #if defined(_AIX) && !defined(MSG_DONTWAIT)
53 #define MSG_DONTWAIT MSG_NONBLOCK
56 #include "modbus-private.h"
58 #include "modbus-tcp.h"
59 #include "modbus-tcp-private.h"
62 static int _modbus_tcp_init_win32(void)
64 /* Initialise Windows Socket API */
67 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0) {
68 fprintf(stderr
, "WSAStartup() returned error code %d\n",
69 (unsigned int)GetLastError());
77 static int _modbus_set_slave(modbus_t
*ctx
, int slave
)
79 /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
80 if (slave
>= 0 && slave
<= 247) {
82 } else if (slave
== MODBUS_TCP_SLAVE
) {
83 /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
84 * restore the default value. */
94 /* Builds a TCP request header */
95 static int _modbus_tcp_build_request_basis(modbus_t
*ctx
, int function
,
99 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
101 /* Increase transaction ID */
102 if (ctx_tcp
->t_id
< UINT16_MAX
)
106 req
[0] = ctx_tcp
->t_id
>> 8;
107 req
[1] = ctx_tcp
->t_id
& 0x00ff;
109 /* Protocol Modbus */
113 /* Length will be defined later by set_req_length_tcp at offsets 4
119 req
[9] = addr
& 0x00ff;
121 req
[11] = nb
& 0x00ff;
123 return _MODBUS_TCP_PRESET_REQ_LENGTH
;
126 /* Builds a TCP response header */
127 static int _modbus_tcp_build_response_basis(sft_t
*sft
, uint8_t *rsp
)
129 /* Extract from MODBUS Messaging on TCP/IP Implementation
130 Guide V1.0b (page 23/46):
131 The transaction identifier is used to associate the future
132 response with the request. */
133 rsp
[0] = sft
->t_id
>> 8;
134 rsp
[1] = sft
->t_id
& 0x00ff;
136 /* Protocol Modbus */
140 /* Length will be set later by send_msg (4 and 5) */
142 /* The slave ID is copied from the indication */
144 rsp
[7] = sft
->function
;
146 return _MODBUS_TCP_PRESET_RSP_LENGTH
;
150 static int _modbus_tcp_prepare_response_tid(const uint8_t *req
, int *req_length
)
152 return (req
[0] << 8) + req
[1];
155 static int _modbus_tcp_send_msg_pre(uint8_t *req
, int req_length
)
157 /* Subtract the header length to the message length */
158 int mbap_length
= req_length
- 6;
160 req
[4] = mbap_length
>> 8;
161 req
[5] = mbap_length
& 0x00FF;
166 static ssize_t
_modbus_tcp_send(modbus_t
*ctx
, const uint8_t *req
, int req_length
)
169 Requests not to send SIGPIPE on errors on stream oriented
170 sockets when the other end breaks the connection. The EPIPE
171 error is still returned. */
172 return send(ctx
->s
, (const char *)req
, req_length
, MSG_NOSIGNAL
);
175 static int _modbus_tcp_receive(modbus_t
*ctx
, uint8_t *req
) {
176 return _modbus_receive_msg(ctx
, req
, MSG_INDICATION
);
179 static ssize_t
_modbus_tcp_recv(modbus_t
*ctx
, uint8_t *rsp
, int rsp_length
) {
180 return recv(ctx
->s
, (char *)rsp
, rsp_length
, 0);
183 static int _modbus_tcp_check_integrity(modbus_t
*ctx
, uint8_t *msg
, const int msg_length
)
188 static int _modbus_tcp_pre_check_confirmation(modbus_t
*ctx
, const uint8_t *req
,
189 const uint8_t *rsp
, int rsp_length
)
191 /* Check transaction ID */
192 if (req
[0] != rsp
[0] || req
[1] != rsp
[1]) {
194 fprintf(stderr
, "Invalid transaction ID received 0x%X (not 0x%X)\n",
195 (rsp
[0] << 8) + rsp
[1], (req
[0] << 8) + req
[1]);
201 /* Check protocol ID */
202 if (rsp
[2] != 0x0 && rsp
[3] != 0x0) {
204 fprintf(stderr
, "Invalid protocol ID received 0x%X (not 0x0)\n",
205 (rsp
[2] << 8) + rsp
[3]);
214 static int _modbus_tcp_set_ipv4_options(int s
)
219 /* Set the TCP no delay flag */
220 /* SOL_TCP = IPPROTO_TCP */
222 rc
= setsockopt(s
, IPPROTO_TCP
, TCP_NODELAY
,
223 (const void *)&option
, sizeof(int));
228 /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
229 * make sockets non-blocking */
230 /* Do not care about the return value, this is optional */
231 #if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
234 /* Setting FIONBIO expects an unsigned long according to MSDN */
236 ioctlsocket(s
, FIONBIO
, &loption
);
240 ioctl(s
, FIONBIO
, &option
);
246 * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
247 * necessary to workaround that problem.
249 /* Set the IP low delay option */
250 option
= IPTOS_LOWDELAY
;
251 rc
= setsockopt(s
, IPPROTO_IP
, IP_TOS
,
252 (const void *)&option
, sizeof(int));
261 static int _connect(int sockfd
, const struct sockaddr
*addr
, socklen_t addrlen
,
262 const struct timeval
*ro_tv
)
264 int rc
= connect(sockfd
, addr
, addrlen
);
269 wsaError
= WSAGetLastError();
272 if (wsaError
== WSAEWOULDBLOCK
|| wsaError
== WSAEINPROGRESS
) {
274 if (rc
== -1 && errno
== EINPROGRESS
) {
278 socklen_t optlen
= sizeof(optval
);
279 struct timeval tv
= *ro_tv
;
281 /* Wait to be available in writing */
283 FD_SET(sockfd
, &wset
);
284 rc
= select(sockfd
+ 1, NULL
, &wset
, NULL
, &tv
);
286 /* Timeout or fail */
290 /* The connection is established if SO_ERROR and optval are set to 0 */
291 rc
= getsockopt(sockfd
, SOL_SOCKET
, SO_ERROR
, (void *)&optval
, &optlen
);
292 if (rc
== 0 && optval
== 0) {
295 errno
= ECONNREFUSED
;
302 /* Establishes a modbus TCP connection with a Modbus server. */
303 static int _modbus_tcp_connect(modbus_t
*ctx
)
306 /* Specialized version of sockaddr for Internet socket address (same size) */
307 struct sockaddr_in addr
;
308 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
309 int flags
= SOCK_STREAM
;
312 if (_modbus_tcp_init_win32() == -1) {
318 flags
|= SOCK_CLOEXEC
;
322 flags
|= SOCK_NONBLOCK
;
325 ctx
->s
= socket(PF_INET
, flags
, 0);
330 rc
= _modbus_tcp_set_ipv4_options(ctx
->s
);
338 printf("Connecting to %s:%d\n", ctx_tcp
->ip
, ctx_tcp
->port
);
341 addr
.sin_family
= AF_INET
;
342 addr
.sin_port
= htons(ctx_tcp
->port
);
343 addr
.sin_addr
.s_addr
= inet_addr(ctx_tcp
->ip
);
344 rc
= _connect(ctx
->s
, (struct sockaddr
*)&addr
, sizeof(addr
), &ctx
->response_timeout
);
354 /* Establishes a modbus TCP PI connection with a Modbus server. */
355 static int _modbus_tcp_pi_connect(modbus_t
*ctx
)
358 struct addrinfo
*ai_list
;
359 struct addrinfo
*ai_ptr
;
360 struct addrinfo ai_hints
;
361 modbus_tcp_pi_t
*ctx_tcp_pi
= ctx
->backend_data
;
364 if (_modbus_tcp_init_win32() == -1) {
369 memset(&ai_hints
, 0, sizeof(ai_hints
));
371 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
373 ai_hints
.ai_family
= AF_UNSPEC
;
374 ai_hints
.ai_socktype
= SOCK_STREAM
;
375 ai_hints
.ai_addr
= NULL
;
376 ai_hints
.ai_canonname
= NULL
;
377 ai_hints
.ai_next
= NULL
;
380 rc
= getaddrinfo(ctx_tcp_pi
->node
, ctx_tcp_pi
->service
,
381 &ai_hints
, &ai_list
);
384 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
386 errno
= ECONNREFUSED
;
390 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
391 int flags
= ai_ptr
->ai_socktype
;
395 flags
|= SOCK_CLOEXEC
;
399 flags
|= SOCK_NONBLOCK
;
402 s
= socket(ai_ptr
->ai_family
, flags
, ai_ptr
->ai_protocol
);
406 if (ai_ptr
->ai_family
== AF_INET
)
407 _modbus_tcp_set_ipv4_options(s
);
410 printf("Connecting to [%s]:%s\n", ctx_tcp_pi
->node
, ctx_tcp_pi
->service
);
413 rc
= _connect(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
, &ctx
->response_timeout
);
423 freeaddrinfo(ai_list
);
432 /* Closes the network connection and socket in TCP mode */
433 static void _modbus_tcp_close(modbus_t
*ctx
)
436 shutdown(ctx
->s
, SHUT_RDWR
);
442 static int _modbus_tcp_flush(modbus_t
*ctx
)
448 /* Extract the garbage from the socket */
449 char devnull
[MODBUS_TCP_MAX_ADU_LENGTH
];
451 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, MSG_DONTWAIT
);
453 /* On Win32, it's a bit more complicated to not wait */
460 FD_SET(ctx
->s
, &rset
);
461 rc
= select(ctx
->s
+1, &rset
, NULL
, NULL
, &tv
);
467 /* There is data to flush */
468 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, 0);
474 } while (rc
== MODBUS_TCP_MAX_ADU_LENGTH
);
479 /* Listens for any request from one or many modbus masters in TCP */
480 int modbus_tcp_listen(modbus_t
*ctx
, int nb_connection
)
485 struct sockaddr_in addr
;
486 modbus_tcp_t
*ctx_tcp
;
493 ctx_tcp
= ctx
->backend_data
;
496 if (_modbus_tcp_init_win32() == -1) {
504 flags
|= SOCK_CLOEXEC
;
507 new_s
= socket(PF_INET
, flags
, IPPROTO_TCP
);
513 if (setsockopt(new_s
, SOL_SOCKET
, SO_REUSEADDR
,
514 (char *)&enable
, sizeof(enable
)) == -1) {
519 memset(&addr
, 0, sizeof(addr
));
520 addr
.sin_family
= AF_INET
;
521 /* If the modbus port is < to 1024, we need the setuid root. */
522 addr
.sin_port
= htons(ctx_tcp
->port
);
523 if (ctx_tcp
->ip
[0] == '0') {
524 /* Listen any addresses */
525 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
527 /* Listen only specified IP address */
528 addr
.sin_addr
.s_addr
= inet_addr(ctx_tcp
->ip
);
530 if (bind(new_s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == -1) {
535 if (listen(new_s
, nb_connection
) == -1) {
543 int modbus_tcp_pi_listen(modbus_t
*ctx
, int nb_connection
)
546 struct addrinfo
*ai_list
;
547 struct addrinfo
*ai_ptr
;
548 struct addrinfo ai_hints
;
552 modbus_tcp_pi_t
*ctx_tcp_pi
;
559 ctx_tcp_pi
= ctx
->backend_data
;
562 if (_modbus_tcp_init_win32() == -1) {
567 if (ctx_tcp_pi
->node
[0] == 0) {
568 node
= NULL
; /* == any */
570 node
= ctx_tcp_pi
->node
;
573 if (ctx_tcp_pi
->service
[0] == 0) {
576 service
= ctx_tcp_pi
->service
;
579 memset(&ai_hints
, 0, sizeof (ai_hints
));
580 /* If node is not NULL, than the AI_PASSIVE flag is ignored. */
581 ai_hints
.ai_flags
|= AI_PASSIVE
;
583 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
585 ai_hints
.ai_family
= AF_UNSPEC
;
586 ai_hints
.ai_socktype
= SOCK_STREAM
;
587 ai_hints
.ai_addr
= NULL
;
588 ai_hints
.ai_canonname
= NULL
;
589 ai_hints
.ai_next
= NULL
;
592 rc
= getaddrinfo(node
, service
, &ai_hints
, &ai_list
);
595 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
597 errno
= ECONNREFUSED
;
602 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
603 int flags
= ai_ptr
->ai_socktype
;
607 flags
|= SOCK_CLOEXEC
;
610 s
= socket(ai_ptr
->ai_family
, flags
, ai_ptr
->ai_protocol
);
618 rc
= setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
619 (void *)&enable
, sizeof (enable
));
623 perror("setsockopt");
629 rc
= bind(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
);
638 rc
= listen(s
, nb_connection
);
650 freeaddrinfo(ai_list
);
659 int modbus_tcp_accept(modbus_t
*ctx
, int *s
)
661 struct sockaddr_in addr
;
669 addrlen
= sizeof(addr
);
671 /* Inherit socket flags and use accept4 call */
672 ctx
->s
= accept4(*s
, (struct sockaddr
*)&addr
, &addrlen
, SOCK_CLOEXEC
);
674 ctx
->s
= accept(*s
, (struct sockaddr
*)&addr
, &addrlen
);
682 printf("The client connection from %s is accepted\n",
683 inet_ntoa(addr
.sin_addr
));
689 int modbus_tcp_pi_accept(modbus_t
*ctx
, int *s
)
691 struct sockaddr_storage addr
;
699 addrlen
= sizeof(addr
);
701 /* Inherit socket flags and use accept4 call */
702 ctx
->s
= accept4(*s
, (struct sockaddr
*)&addr
, &addrlen
, SOCK_CLOEXEC
);
704 ctx
->s
= accept(*s
, (struct sockaddr
*)&addr
, &addrlen
);
712 printf("The client connection is accepted.\n");
718 static int _modbus_tcp_select(modbus_t
*ctx
, fd_set
*rset
, struct timeval
*tv
, int length_to_read
)
721 while ((s_rc
= select(ctx
->s
+1, rset
, NULL
, NULL
, tv
)) == -1) {
722 if (errno
== EINTR
) {
724 fprintf(stderr
, "A non blocked signal was caught\n");
726 /* Necessary after an error */
728 FD_SET(ctx
->s
, rset
);
742 static void _modbus_tcp_free(modbus_t
*ctx
) {
743 free(ctx
->backend_data
);
747 const modbus_backend_t _modbus_tcp_backend
= {
748 _MODBUS_BACKEND_TYPE_TCP
,
749 _MODBUS_TCP_HEADER_LENGTH
,
750 _MODBUS_TCP_CHECKSUM_LENGTH
,
751 MODBUS_TCP_MAX_ADU_LENGTH
,
753 _modbus_tcp_build_request_basis
,
754 _modbus_tcp_build_response_basis
,
755 _modbus_tcp_prepare_response_tid
,
756 _modbus_tcp_send_msg_pre
,
760 _modbus_tcp_check_integrity
,
761 _modbus_tcp_pre_check_confirmation
,
770 const modbus_backend_t _modbus_tcp_pi_backend
= {
771 _MODBUS_BACKEND_TYPE_TCP
,
772 _MODBUS_TCP_HEADER_LENGTH
,
773 _MODBUS_TCP_CHECKSUM_LENGTH
,
774 MODBUS_TCP_MAX_ADU_LENGTH
,
776 _modbus_tcp_build_request_basis
,
777 _modbus_tcp_build_response_basis
,
778 _modbus_tcp_prepare_response_tid
,
779 _modbus_tcp_send_msg_pre
,
783 _modbus_tcp_check_integrity
,
784 _modbus_tcp_pre_check_confirmation
,
785 _modbus_tcp_pi_connect
,
792 modbus_t
* modbus_new_tcp(const char *ip
, int port
)
795 modbus_tcp_t
*ctx_tcp
;
800 /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
801 handler for SIGPIPE. */
804 sa
.sa_handler
= SIG_IGN
;
805 if (sigaction(SIGPIPE
, &sa
, NULL
) < 0) {
806 /* The debug flag can't be set here... */
807 fprintf(stderr
, "Could not install SIGPIPE handler.\n");
812 ctx
= (modbus_t
*)malloc(sizeof(modbus_t
));
816 _modbus_init_common(ctx
);
818 /* Could be changed after to reach a remote serial Modbus device */
819 ctx
->slave
= MODBUS_TCP_SLAVE
;
821 ctx
->backend
= &_modbus_tcp_backend
;
823 ctx
->backend_data
= (modbus_tcp_t
*)malloc(sizeof(modbus_tcp_t
));
824 if (ctx
->backend_data
== NULL
) {
829 ctx_tcp
= (modbus_tcp_t
*)ctx
->backend_data
;
832 dest_size
= sizeof(char) * 16;
833 ret_size
= strlcpy(ctx_tcp
->ip
, ip
, dest_size
);
835 fprintf(stderr
, "The IP string is empty\n");
841 if (ret_size
>= dest_size
) {
842 fprintf(stderr
, "The IP string has been truncated\n");
848 ctx_tcp
->ip
[0] = '0';
850 ctx_tcp
->port
= port
;
857 modbus_t
* modbus_new_tcp_pi(const char *node
, const char *service
)
860 modbus_tcp_pi_t
*ctx_tcp_pi
;
864 ctx
= (modbus_t
*)malloc(sizeof(modbus_t
));
868 _modbus_init_common(ctx
);
870 /* Could be changed after to reach a remote serial Modbus device */
871 ctx
->slave
= MODBUS_TCP_SLAVE
;
873 ctx
->backend
= &_modbus_tcp_pi_backend
;
875 ctx
->backend_data
= (modbus_tcp_pi_t
*)malloc(sizeof(modbus_tcp_pi_t
));
876 if (ctx
->backend_data
== NULL
) {
881 ctx_tcp_pi
= (modbus_tcp_pi_t
*)ctx
->backend_data
;
884 /* The node argument can be empty to indicate any hosts */
885 ctx_tcp_pi
->node
[0] = 0;
887 dest_size
= sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH
;
888 ret_size
= strlcpy(ctx_tcp_pi
->node
, node
, dest_size
);
890 fprintf(stderr
, "The node string is empty\n");
896 if (ret_size
>= dest_size
) {
897 fprintf(stderr
, "The node string has been truncated\n");
904 if (service
!= NULL
) {
905 dest_size
= sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH
;
906 ret_size
= strlcpy(ctx_tcp_pi
->service
, service
, dest_size
);
908 /* Empty service is not allowed, error caught below. */
913 fprintf(stderr
, "The service string is empty\n");
919 if (ret_size
>= dest_size
) {
920 fprintf(stderr
, "The service string has been truncated\n");
926 ctx_tcp_pi
->t_id
= 0;