2 * Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
4 * SPDX-License-Identifier: LGPL-2.1-or-later
10 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
11 * minwg32 headers check WINVER before allowing the use of these */
13 # define WINVER 0x0501
27 #include <sys/types.h>
30 /* Already set in modbus-tcp.h but it seems order matters in VS2005 */
31 # include <winsock2.h>
32 # include <ws2tcpip.h>
34 # define close closesocket
35 # define strdup _strdup
37 # include <sys/socket.h>
38 # include <sys/ioctl.h>
40 #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
42 # include <netinet/in_systm.h>
45 # include <netinet/in.h>
46 # include <netinet/ip.h>
47 # include <netinet/tcp.h>
48 # include <arpa/inet.h>
52 #if !defined(MSG_NOSIGNAL)
53 #define MSG_NOSIGNAL 0
56 #if defined(_AIX) && !defined(MSG_DONTWAIT)
57 #define MSG_DONTWAIT MSG_NONBLOCK
61 #include "modbus-private.h"
63 #include "modbus-tcp-private.h"
64 #include "modbus-tcp.h"
67 static int _modbus_tcp_init_win32(void)
69 /* Initialise Windows Socket API */
72 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0) {
74 "WSAStartup() returned error code %d\n",
75 (unsigned int) GetLastError());
83 static int _modbus_set_slave(modbus_t
*ctx
, int slave
)
85 int max_slave
= (ctx
->quirks
& MODBUS_QUIRK_MAX_SLAVE
) ? 255 : 247;
87 /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
88 if (slave
>= 0 && slave
<= max_slave
) {
90 } else if (slave
== MODBUS_TCP_SLAVE
) {
91 /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
92 * restore the default value. */
102 /* Builds a TCP request header */
103 static int _modbus_tcp_build_request_basis(
104 modbus_t
*ctx
, int function
, int addr
, int nb
, uint8_t *req
)
106 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
108 /* Increase transaction ID */
109 if (ctx_tcp
->t_id
< UINT16_MAX
)
113 req
[0] = ctx_tcp
->t_id
>> 8;
114 req
[1] = ctx_tcp
->t_id
& 0x00ff;
116 /* Protocol Modbus */
120 /* Length will be defined later by set_req_length_tcp at offsets 4
126 req
[9] = addr
& 0x00ff;
128 req
[11] = nb
& 0x00ff;
130 return _MODBUS_TCP_PRESET_REQ_LENGTH
;
133 /* Builds a TCP response header */
134 static int _modbus_tcp_build_response_basis(sft_t
*sft
, uint8_t *rsp
)
136 /* Extract from MODBUS Messaging on TCP/IP Implementation
137 Guide V1.0b (page 23/46):
138 The transaction identifier is used to associate the future
139 response with the request. */
140 rsp
[0] = sft
->t_id
>> 8;
141 rsp
[1] = sft
->t_id
& 0x00ff;
143 /* Protocol Modbus */
147 /* Length will be set later by send_msg (4 and 5) */
149 /* The slave ID is copied from the indication */
151 rsp
[7] = sft
->function
;
153 return _MODBUS_TCP_PRESET_RSP_LENGTH
;
156 static int _modbus_tcp_get_response_tid(const uint8_t *req
)
158 return (req
[0] << 8) + req
[1];
161 static int _modbus_tcp_send_msg_pre(uint8_t *req
, int req_length
)
163 /* Subtract the header length to the message length */
164 int mbap_length
= req_length
- 6;
166 req
[4] = mbap_length
>> 8;
167 req
[5] = mbap_length
& 0x00FF;
172 static ssize_t
_modbus_tcp_send(modbus_t
*ctx
, const uint8_t *req
, int req_length
)
175 Requests not to send SIGPIPE on errors on stream oriented
176 sockets when the other end breaks the connection. The EPIPE
177 error is still returned. */
178 return send(ctx
->s
, (const char *) req
, req_length
, MSG_NOSIGNAL
);
181 static int _modbus_tcp_receive(modbus_t
*ctx
, uint8_t *req
)
183 return _modbus_receive_msg(ctx
, req
, MSG_INDICATION
);
186 static ssize_t
_modbus_tcp_recv(modbus_t
*ctx
, uint8_t *rsp
, int rsp_length
)
188 return recv(ctx
->s
, (char *) rsp
, rsp_length
, 0);
191 static int _modbus_tcp_check_integrity(modbus_t
*ctx
, uint8_t *msg
, const int msg_length
)
196 static int _modbus_tcp_pre_check_confirmation(modbus_t
*ctx
,
201 unsigned int protocol_id
;
202 /* Check transaction ID */
203 if (req
[0] != rsp
[0] || req
[1] != rsp
[1]) {
206 "Invalid transaction ID received 0x%X (not 0x%X)\n",
207 (rsp
[0] << 8) + rsp
[1],
208 (req
[0] << 8) + req
[1]);
214 /* Check protocol ID */
215 protocol_id
= (rsp
[2] << 8) + rsp
[3];
216 if (protocol_id
!= 0x0) {
218 fprintf(stderr
, "Invalid protocol ID received 0x%X (not 0x0)\n", protocol_id
);
227 static int _modbus_tcp_set_ipv4_options(int s
)
232 /* Set the TCP no delay flag */
233 /* SOL_TCP = IPPROTO_TCP */
235 rc
= setsockopt(s
, IPPROTO_TCP
, TCP_NODELAY
, &option
, sizeof(int));
240 /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
241 * make sockets non-blocking */
242 /* Do not care about the return value, this is optional */
243 #if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
246 /* Setting FIONBIO expects an unsigned long according to MSDN */
248 ioctlsocket(s
, FIONBIO
, &loption
);
252 ioctl(s
, FIONBIO
, &option
);
258 * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
259 * necessary to workaround that problem.
261 /* Set the IP low delay option */
262 option
= IPTOS_LOWDELAY
;
263 rc
= setsockopt(s
, IPPROTO_IP
, IP_TOS
, &option
, sizeof(int));
272 static int _connect(int sockfd
,
273 const struct sockaddr
*addr
,
275 const struct timeval
*ro_tv
)
277 int rc
= connect(sockfd
, addr
, addrlen
);
282 wsaError
= WSAGetLastError();
285 if (wsaError
== WSAEWOULDBLOCK
|| wsaError
== WSAEINPROGRESS
) {
287 if (rc
== -1 && errno
== EINPROGRESS
) {
291 socklen_t optlen
= sizeof(optval
);
292 struct timeval tv
= *ro_tv
;
294 /* Wait to be available in writing */
296 FD_SET(sockfd
, &wset
);
297 rc
= select(sockfd
+ 1, NULL
, &wset
, NULL
, &tv
);
309 /* The connection is established if SO_ERROR and optval are set to 0 */
310 rc
= getsockopt(sockfd
, SOL_SOCKET
, SO_ERROR
, (void *) &optval
, &optlen
);
311 if (rc
== 0 && optval
== 0) {
314 errno
= ECONNREFUSED
;
321 /* Establishes a modbus TCP connection with a Modbus server. */
322 static int _modbus_tcp_connect(modbus_t
*ctx
)
325 /* Specialized version of sockaddr for Internet socket address (same size) */
326 struct sockaddr_in addr
;
327 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
328 int flags
= SOCK_STREAM
;
331 if (_modbus_tcp_init_win32() == -1) {
337 flags
|= SOCK_CLOEXEC
;
341 flags
|= SOCK_NONBLOCK
;
344 ctx
->s
= socket(PF_INET
, flags
, 0);
349 rc
= _modbus_tcp_set_ipv4_options(ctx
->s
);
357 printf("Connecting to %s:%d\n", ctx_tcp
->ip
, ctx_tcp
->port
);
360 addr
.sin_family
= AF_INET
;
361 addr
.sin_port
= htons(ctx_tcp
->port
);
362 rc
= inet_pton(addr
.sin_family
, ctx_tcp
->ip
, &(addr
.sin_addr
));
365 fprintf(stderr
, "Invalid IP address: %s\n", ctx_tcp
->ip
);
373 _connect(ctx
->s
, (struct sockaddr
*) &addr
, sizeof(addr
), &ctx
->response_timeout
);
383 /* Establishes a modbus TCP PI connection with a Modbus server. */
384 static int _modbus_tcp_pi_connect(modbus_t
*ctx
)
387 struct addrinfo
*ai_list
;
388 struct addrinfo
*ai_ptr
;
389 struct addrinfo ai_hints
;
390 modbus_tcp_pi_t
*ctx_tcp_pi
= ctx
->backend_data
;
393 if (_modbus_tcp_init_win32() == -1) {
398 memset(&ai_hints
, 0, sizeof(ai_hints
));
400 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
402 ai_hints
.ai_family
= AF_UNSPEC
;
403 ai_hints
.ai_socktype
= SOCK_STREAM
;
404 ai_hints
.ai_addr
= NULL
;
405 ai_hints
.ai_canonname
= NULL
;
406 ai_hints
.ai_next
= NULL
;
409 rc
= getaddrinfo(ctx_tcp_pi
->node
, ctx_tcp_pi
->service
, &ai_hints
, &ai_list
);
412 #ifdef HAVE_GAI_STRERROR
413 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
415 fprintf(stderr
, "Error returned by getaddrinfo: %d\n", rc
);
418 freeaddrinfo(ai_list
);
419 errno
= ECONNREFUSED
;
423 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
424 int flags
= ai_ptr
->ai_socktype
;
428 flags
|= SOCK_CLOEXEC
;
432 flags
|= SOCK_NONBLOCK
;
435 s
= socket(ai_ptr
->ai_family
, flags
, ai_ptr
->ai_protocol
);
439 if (ai_ptr
->ai_family
== AF_INET
)
440 _modbus_tcp_set_ipv4_options(s
);
443 printf("Connecting to [%s]:%s\n", ctx_tcp_pi
->node
, ctx_tcp_pi
->service
);
446 rc
= _connect(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
, &ctx
->response_timeout
);
456 freeaddrinfo(ai_list
);
465 static unsigned int _modbus_tcp_is_connected(modbus_t
*ctx
)
470 /* Closes the network connection and socket in TCP mode */
471 static void _modbus_tcp_close(modbus_t
*ctx
)
474 shutdown(ctx
->s
, SHUT_RDWR
);
480 static int _modbus_tcp_flush(modbus_t
*ctx
)
483 // Use an unsigned 16-bit integer to reduce overflow risk. The flush function
484 // is not expected to handle huge amounts of data (> 2GB).
488 /* Extract the garbage from the socket */
489 char devnull
[MODBUS_TCP_MAX_ADU_LENGTH
];
491 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, MSG_DONTWAIT
);
493 /* On Win32, it's a bit more complicated to not wait */
500 FD_SET(ctx
->s
, &rset
);
501 rc
= select(ctx
->s
+ 1, &rset
, NULL
, NULL
, &tv
);
507 /* There is data to flush */
508 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, 0);
512 // Check for overflow before adding
513 if (rc_sum
<= UINT16_MAX
- rc
) {
517 ctx
->error_recovery
= MODBUS_ERROR_RECOVERY_PROTOCOL
;
522 } while (rc
== MODBUS_TCP_MAX_ADU_LENGTH
);
527 /* Listens for any request from one or many modbus masters in TCP */
528 int modbus_tcp_listen(modbus_t
*ctx
, int nb_connection
)
533 struct sockaddr_in addr
;
534 modbus_tcp_t
*ctx_tcp
;
542 ctx_tcp
= ctx
->backend_data
;
545 if (_modbus_tcp_init_win32() == -1) {
553 flags
|= SOCK_CLOEXEC
;
556 new_s
= socket(PF_INET
, flags
, IPPROTO_TCP
);
562 if (setsockopt(new_s
, SOL_SOCKET
, SO_REUSEADDR
, &enable
, sizeof(enable
)) == -1) {
567 memset(&addr
, 0, sizeof(addr
));
568 addr
.sin_family
= AF_INET
;
569 /* If the modbus port is < to 1024, we need the setuid root. */
570 addr
.sin_port
= htons(ctx_tcp
->port
);
571 if (ctx_tcp
->ip
[0] == '0') {
572 /* Listen any addresses */
573 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
575 /* Listen only specified IP address */
576 rc
= inet_pton(addr
.sin_family
, ctx_tcp
->ip
, &(addr
.sin_addr
));
579 fprintf(stderr
, "Invalid IP address: %s\n", ctx_tcp
->ip
);
586 if (bind(new_s
, (struct sockaddr
*) &addr
, sizeof(addr
)) == -1) {
591 if (listen(new_s
, nb_connection
) == -1) {
599 int modbus_tcp_pi_listen(modbus_t
*ctx
, int nb_connection
)
602 struct addrinfo
*ai_list
;
603 struct addrinfo
*ai_ptr
;
604 struct addrinfo ai_hints
;
608 modbus_tcp_pi_t
*ctx_tcp_pi
;
615 ctx_tcp_pi
= ctx
->backend_data
;
618 if (_modbus_tcp_init_win32() == -1) {
623 if (ctx_tcp_pi
->node
[0] == 0) {
624 node
= NULL
; /* == any */
626 node
= ctx_tcp_pi
->node
;
629 if (ctx_tcp_pi
->service
[0] == 0) {
632 service
= ctx_tcp_pi
->service
;
635 memset(&ai_hints
, 0, sizeof(ai_hints
));
636 /* If node is not NULL, than the AI_PASSIVE flag is ignored. */
637 ai_hints
.ai_flags
|= AI_PASSIVE
;
639 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
641 ai_hints
.ai_family
= AF_UNSPEC
;
642 ai_hints
.ai_socktype
= SOCK_STREAM
;
643 ai_hints
.ai_addr
= NULL
;
644 ai_hints
.ai_canonname
= NULL
;
645 ai_hints
.ai_next
= NULL
;
648 rc
= getaddrinfo(node
, service
, &ai_hints
, &ai_list
);
651 #ifdef HAVE_GAI_STRERROR
652 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
654 fprintf(stderr
, "Error returned by getaddrinfo: %d\n", rc
);
657 freeaddrinfo(ai_list
);
658 errno
= ECONNREFUSED
;
663 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
664 int flags
= ai_ptr
->ai_socktype
;
668 flags
|= SOCK_CLOEXEC
;
671 s
= socket(ai_ptr
->ai_family
, flags
, ai_ptr
->ai_protocol
);
679 rc
= setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &enable
, sizeof(enable
));
683 perror("setsockopt");
689 rc
= bind(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
);
698 rc
= listen(s
, nb_connection
);
710 freeaddrinfo(ai_list
);
719 int modbus_tcp_accept(modbus_t
*ctx
, int *s
)
721 struct sockaddr_in addr
;
729 addrlen
= sizeof(addr
);
731 /* Inherit socket flags and use accept4 call */
732 ctx
->s
= accept4(*s
, (struct sockaddr
*) &addr
, &addrlen
, SOCK_CLOEXEC
);
734 ctx
->s
= accept(*s
, (struct sockaddr
*) &addr
, &addrlen
);
742 char buf
[INET_ADDRSTRLEN
];
743 if (inet_ntop(AF_INET
, &(addr
.sin_addr
), buf
, INET_ADDRSTRLEN
) == NULL
) {
744 fprintf(stderr
, "Client connection accepted from unparsable IP.\n");
746 printf("Client connection accepted from %s.\n", buf
);
753 int modbus_tcp_pi_accept(modbus_t
*ctx
, int *s
)
755 struct sockaddr_in6 addr
;
763 addrlen
= sizeof(addr
);
765 /* Inherit socket flags and use accept4 call */
766 ctx
->s
= accept4(*s
, (struct sockaddr
*) &addr
, &addrlen
, SOCK_CLOEXEC
);
768 ctx
->s
= accept(*s
, (struct sockaddr
*) &addr
, &addrlen
);
776 char buf
[INET6_ADDRSTRLEN
];
777 if (inet_ntop(AF_INET6
, &(addr
.sin6_addr
), buf
, INET6_ADDRSTRLEN
) == NULL
) {
778 fprintf(stderr
, "Client connection accepted from unparsable IP.\n");
780 printf("Client connection accepted from %s.\n", buf
);
788 _modbus_tcp_select(modbus_t
*ctx
, fd_set
*rset
, struct timeval
*tv
, int length_to_read
)
791 while ((s_rc
= select(ctx
->s
+ 1, rset
, NULL
, NULL
, tv
)) == -1) {
792 if (errno
== EINTR
) {
794 fprintf(stderr
, "A non blocked signal was caught\n");
796 /* Necessary after an error */
798 FD_SET(ctx
->s
, rset
);
812 static void _modbus_tcp_free(modbus_t
*ctx
)
814 if (ctx
->backend_data
) {
815 free(ctx
->backend_data
);
820 static void _modbus_tcp_pi_free(modbus_t
*ctx
)
822 if (ctx
->backend_data
) {
823 modbus_tcp_pi_t
*ctx_tcp_pi
= ctx
->backend_data
;
824 free(ctx_tcp_pi
->node
);
825 free(ctx_tcp_pi
->service
);
826 free(ctx
->backend_data
);
833 const modbus_backend_t _modbus_tcp_backend
= {
834 _MODBUS_BACKEND_TYPE_TCP
,
835 _MODBUS_TCP_HEADER_LENGTH
,
836 _MODBUS_TCP_CHECKSUM_LENGTH
,
837 MODBUS_TCP_MAX_ADU_LENGTH
,
839 _modbus_tcp_build_request_basis
,
840 _modbus_tcp_build_response_basis
,
841 _modbus_tcp_get_response_tid
,
842 _modbus_tcp_send_msg_pre
,
846 _modbus_tcp_check_integrity
,
847 _modbus_tcp_pre_check_confirmation
,
849 _modbus_tcp_is_connected
,
856 const modbus_backend_t _modbus_tcp_pi_backend
= {
857 _MODBUS_BACKEND_TYPE_TCP
,
858 _MODBUS_TCP_HEADER_LENGTH
,
859 _MODBUS_TCP_CHECKSUM_LENGTH
,
860 MODBUS_TCP_MAX_ADU_LENGTH
,
862 _modbus_tcp_build_request_basis
,
863 _modbus_tcp_build_response_basis
,
864 _modbus_tcp_get_response_tid
,
865 _modbus_tcp_send_msg_pre
,
869 _modbus_tcp_check_integrity
,
870 _modbus_tcp_pre_check_confirmation
,
871 _modbus_tcp_pi_connect
,
872 _modbus_tcp_is_connected
,
881 modbus_t
*modbus_new_tcp(const char *ip
, int port
)
884 modbus_tcp_t
*ctx_tcp
;
889 /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
890 handler for SIGPIPE. */
893 sa
.sa_handler
= SIG_IGN
;
894 if (sigaction(SIGPIPE
, &sa
, NULL
) < 0) {
895 /* The debug flag can't be set here... */
896 fprintf(stderr
, "Could not install SIGPIPE handler.\n");
901 ctx
= (modbus_t
*) malloc(sizeof(modbus_t
));
905 _modbus_init_common(ctx
);
907 /* Could be changed after to reach a remote serial Modbus device */
908 ctx
->slave
= MODBUS_TCP_SLAVE
;
910 ctx
->backend
= &_modbus_tcp_backend
;
912 ctx
->backend_data
= (modbus_tcp_t
*) malloc(sizeof(modbus_tcp_t
));
913 if (ctx
->backend_data
== NULL
) {
918 ctx_tcp
= (modbus_tcp_t
*) ctx
->backend_data
;
921 dest_size
= sizeof(char) * 16;
922 ret_size
= strlcpy(ctx_tcp
->ip
, ip
, dest_size
);
924 fprintf(stderr
, "The IP string is empty\n");
930 if (ret_size
>= dest_size
) {
931 fprintf(stderr
, "The IP string has been truncated\n");
937 ctx_tcp
->ip
[0] = '0';
939 ctx_tcp
->port
= port
;
945 modbus_t
*modbus_new_tcp_pi(const char *node
, const char *service
)
948 modbus_tcp_pi_t
*ctx_tcp_pi
;
950 ctx
= (modbus_t
*) malloc(sizeof(modbus_t
));
954 _modbus_init_common(ctx
);
956 /* Could be changed after to reach a remote serial Modbus device */
957 ctx
->slave
= MODBUS_TCP_SLAVE
;
959 ctx
->backend
= &_modbus_tcp_pi_backend
;
961 ctx
->backend_data
= (modbus_tcp_pi_t
*) malloc(sizeof(modbus_tcp_pi_t
));
962 if (ctx
->backend_data
== NULL
) {
967 ctx_tcp_pi
= (modbus_tcp_pi_t
*) ctx
->backend_data
;
968 ctx_tcp_pi
->node
= NULL
;
969 ctx_tcp_pi
->service
= NULL
;
972 ctx_tcp_pi
->node
= strdup(node
);
974 /* The node argument can be empty to indicate any hosts */
975 ctx_tcp_pi
->node
= strdup("");
978 if (ctx_tcp_pi
->node
== NULL
) {
984 if (service
!= NULL
&& service
[0] != '\0') {
985 ctx_tcp_pi
->service
= strdup(service
);
987 /* Default Modbus port number */
988 ctx_tcp_pi
->service
= strdup("502");
991 if (ctx_tcp_pi
->service
== NULL
) {
997 ctx_tcp_pi
->t_id
= 0;