2 * Copyright © 2001-2013 Stéphane Raimbault <stephane.raimbault@gmail.com>
4 * SPDX-License-Identifier: LGPL-2.1+
15 #include <sys/types.h>
19 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
20 * minwg32 headers check WINVER before allowing the use of these */
22 # define WINVER 0x0501
24 /* Already set in modbus-tcp.h but it seems order matters in VS2005 */
25 # include <winsock2.h>
26 # include <ws2tcpip.h>
28 # define close closesocket
30 # include <sys/socket.h>
31 # include <sys/ioctl.h>
33 #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
35 # include <netinet/in_systm.h>
38 # include <netinet/in.h>
39 # include <netinet/ip.h>
40 # include <netinet/tcp.h>
41 # include <arpa/inet.h>
45 #if !defined(MSG_NOSIGNAL)
46 #define MSG_NOSIGNAL 0
49 #if defined(_AIX) && !defined(MSG_DONTWAIT)
50 #define MSG_DONTWAIT MSG_NONBLOCK
53 #include "modbus-private.h"
55 #include "modbus-tcp.h"
56 #include "modbus-tcp-private.h"
59 static int _modbus_tcp_init_win32(void)
61 /* Initialise Windows Socket API */
64 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0) {
65 fprintf(stderr
, "WSAStartup() returned error code %d\n",
66 (unsigned int)GetLastError());
74 static int _modbus_set_slave(modbus_t
*ctx
, int slave
)
76 /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
77 if (slave
>= 0 && slave
<= 247) {
79 } else if (slave
== MODBUS_TCP_SLAVE
) {
80 /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
81 * restore the default value. */
91 /* Builds a TCP request header */
92 static int _modbus_tcp_build_request_basis(modbus_t
*ctx
, int function
,
96 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
98 /* Increase transaction ID */
99 if (ctx_tcp
->t_id
< UINT16_MAX
)
103 req
[0] = ctx_tcp
->t_id
>> 8;
104 req
[1] = ctx_tcp
->t_id
& 0x00ff;
106 /* Protocol Modbus */
110 /* Length will be defined later by set_req_length_tcp at offsets 4
116 req
[9] = addr
& 0x00ff;
118 req
[11] = nb
& 0x00ff;
120 return _MODBUS_TCP_PRESET_REQ_LENGTH
;
123 /* Builds a TCP response header */
124 static int _modbus_tcp_build_response_basis(sft_t
*sft
, uint8_t *rsp
)
126 /* Extract from MODBUS Messaging on TCP/IP Implementation
127 Guide V1.0b (page 23/46):
128 The transaction identifier is used to associate the future
129 response with the request. */
130 rsp
[0] = sft
->t_id
>> 8;
131 rsp
[1] = sft
->t_id
& 0x00ff;
133 /* Protocol Modbus */
137 /* Length will be set later by send_msg (4 and 5) */
139 /* The slave ID is copied from the indication */
141 rsp
[7] = sft
->function
;
143 return _MODBUS_TCP_PRESET_RSP_LENGTH
;
147 static int _modbus_tcp_prepare_response_tid(const uint8_t *req
, int *req_length
)
149 return (req
[0] << 8) + req
[1];
152 static int _modbus_tcp_send_msg_pre(uint8_t *req
, int req_length
)
154 /* Substract the header length to the message length */
155 int mbap_length
= req_length
- 6;
157 req
[4] = mbap_length
>> 8;
158 req
[5] = mbap_length
& 0x00FF;
163 static ssize_t
_modbus_tcp_send(modbus_t
*ctx
, const uint8_t *req
, int req_length
)
166 Requests not to send SIGPIPE on errors on stream oriented
167 sockets when the other end breaks the connection. The EPIPE
168 error is still returned. */
169 return send(ctx
->s
, (const char *)req
, req_length
, MSG_NOSIGNAL
);
172 static int _modbus_tcp_receive(modbus_t
*ctx
, uint8_t *req
) {
173 return _modbus_receive_msg(ctx
, req
, MSG_INDICATION
);
176 static ssize_t
_modbus_tcp_recv(modbus_t
*ctx
, uint8_t *rsp
, int rsp_length
) {
177 return recv(ctx
->s
, (char *)rsp
, rsp_length
, 0);
180 static int _modbus_tcp_check_integrity(modbus_t
*ctx
, uint8_t *msg
, const int msg_length
)
185 static int _modbus_tcp_pre_check_confirmation(modbus_t
*ctx
, const uint8_t *req
,
186 const uint8_t *rsp
, int rsp_length
)
188 /* Check transaction ID */
189 if (req
[0] != rsp
[0] || req
[1] != rsp
[1]) {
191 fprintf(stderr
, "Invalid transaction ID received 0x%X (not 0x%X)\n",
192 (rsp
[0] << 8) + rsp
[1], (req
[0] << 8) + req
[1]);
198 /* Check protocol ID */
199 if (rsp
[2] != 0x0 && rsp
[3] != 0x0) {
201 fprintf(stderr
, "Invalid protocol ID received 0x%X (not 0x0)\n",
202 (rsp
[2] << 8) + rsp
[3]);
211 static int _modbus_tcp_set_ipv4_options(int s
)
216 /* Set the TCP no delay flag */
217 /* SOL_TCP = IPPROTO_TCP */
219 rc
= setsockopt(s
, IPPROTO_TCP
, TCP_NODELAY
,
220 (const void *)&option
, sizeof(int));
225 /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
226 * make sockets non-blocking */
227 /* Do not care about the return value, this is optional */
229 #if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
232 /* Setting FIONBIO expects an unsigned long according to MSDN */
234 ioctlsocket(s
, FIONBIO
, &loption
);
237 ioctl(s
, FIONBIO
, &option
);
243 * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
244 * necessary to workaround that problem.
246 /* Set the IP low delay option */
247 option
= IPTOS_LOWDELAY
;
248 rc
= setsockopt(s
, IPPROTO_IP
, IP_TOS
,
249 (const void *)&option
, sizeof(int));
258 static int _connect(int sockfd
, const struct sockaddr
*addr
, socklen_t addrlen
,
259 const struct timeval
*ro_tv
)
261 int rc
= connect(sockfd
, addr
, addrlen
);
266 wsaError
= WSAGetLastError();
269 if (wsaError
== WSAEWOULDBLOCK
|| wsaError
== WSAEINPROGRESS
) {
271 if (rc
== -1 && errno
== EINPROGRESS
) {
275 socklen_t optlen
= sizeof(optval
);
276 struct timeval tv
= *ro_tv
;
278 /* Wait to be available in writing */
280 FD_SET(sockfd
, &wset
);
281 rc
= select(sockfd
+ 1, NULL
, &wset
, NULL
, &tv
);
283 /* Timeout or fail */
287 /* The connection is established if SO_ERROR and optval are set to 0 */
288 rc
= getsockopt(sockfd
, SOL_SOCKET
, SO_ERROR
, (void *)&optval
, &optlen
);
289 if (rc
== 0 && optval
== 0) {
292 errno
= ECONNREFUSED
;
299 /* Establishes a modbus TCP connection with a Modbus server. */
300 static int _modbus_tcp_connect(modbus_t
*ctx
)
303 /* Specialized version of sockaddr for Internet socket address (same size) */
304 struct sockaddr_in addr
;
305 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
306 int flags
= SOCK_STREAM
;
309 if (_modbus_tcp_init_win32() == -1) {
315 flags
|= SOCK_CLOEXEC
;
319 flags
|= SOCK_NONBLOCK
;
322 ctx
->s
= socket(PF_INET
, flags
, 0);
327 rc
= _modbus_tcp_set_ipv4_options(ctx
->s
);
335 printf("Connecting to %s:%d\n", ctx_tcp
->ip
, ctx_tcp
->port
);
338 addr
.sin_family
= AF_INET
;
339 addr
.sin_port
= htons(ctx_tcp
->port
);
340 addr
.sin_addr
.s_addr
= inet_addr(ctx_tcp
->ip
);
341 rc
= _connect(ctx
->s
, (struct sockaddr
*)&addr
, sizeof(addr
), &ctx
->response_timeout
);
351 /* Establishes a modbus TCP PI connection with a Modbus server. */
352 static int _modbus_tcp_pi_connect(modbus_t
*ctx
)
355 struct addrinfo
*ai_list
;
356 struct addrinfo
*ai_ptr
;
357 struct addrinfo ai_hints
;
358 modbus_tcp_pi_t
*ctx_tcp_pi
= ctx
->backend_data
;
361 if (_modbus_tcp_init_win32() == -1) {
366 memset(&ai_hints
, 0, sizeof(ai_hints
));
368 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
370 ai_hints
.ai_family
= AF_UNSPEC
;
371 ai_hints
.ai_socktype
= SOCK_STREAM
;
372 ai_hints
.ai_addr
= NULL
;
373 ai_hints
.ai_canonname
= NULL
;
374 ai_hints
.ai_next
= NULL
;
377 rc
= getaddrinfo(ctx_tcp_pi
->node
, ctx_tcp_pi
->service
,
378 &ai_hints
, &ai_list
);
381 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
383 errno
= ECONNREFUSED
;
387 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
388 int flags
= ai_ptr
->ai_socktype
;
392 flags
|= SOCK_CLOEXEC
;
396 flags
|= SOCK_NONBLOCK
;
399 s
= socket(ai_ptr
->ai_family
, flags
, ai_ptr
->ai_protocol
);
403 if (ai_ptr
->ai_family
== AF_INET
)
404 _modbus_tcp_set_ipv4_options(s
);
407 printf("Connecting to [%s]:%s\n", ctx_tcp_pi
->node
, ctx_tcp_pi
->service
);
410 rc
= _connect(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
, &ctx
->response_timeout
);
420 freeaddrinfo(ai_list
);
429 /* Closes the network connection and socket in TCP mode */
430 static void _modbus_tcp_close(modbus_t
*ctx
)
433 shutdown(ctx
->s
, SHUT_RDWR
);
439 static int _modbus_tcp_flush(modbus_t
*ctx
)
445 /* Extract the garbage from the socket */
446 char devnull
[MODBUS_TCP_MAX_ADU_LENGTH
];
448 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, MSG_DONTWAIT
);
450 /* On Win32, it's a bit more complicated to not wait */
457 FD_SET(ctx
->s
, &rset
);
458 rc
= select(ctx
->s
+1, &rset
, NULL
, NULL
, &tv
);
464 /* There is data to flush */
465 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, 0);
471 } while (rc
== MODBUS_TCP_MAX_ADU_LENGTH
);
476 /* Listens for any request from one or many modbus masters in TCP */
477 int modbus_tcp_listen(modbus_t
*ctx
, int nb_connection
)
481 struct sockaddr_in addr
;
482 modbus_tcp_t
*ctx_tcp
;
489 ctx_tcp
= ctx
->backend_data
;
492 if (_modbus_tcp_init_win32() == -1) {
497 new_s
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
503 if (setsockopt(new_s
, SOL_SOCKET
, SO_REUSEADDR
,
504 (char *)&enable
, sizeof(enable
)) == -1) {
509 memset(&addr
, 0, sizeof(addr
));
510 addr
.sin_family
= AF_INET
;
511 /* If the modbus port is < to 1024, we need the setuid root. */
512 addr
.sin_port
= htons(ctx_tcp
->port
);
513 if (ctx_tcp
->ip
[0] == '0') {
514 /* Listen any addresses */
515 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
517 /* Listen only specified IP address */
518 addr
.sin_addr
.s_addr
= inet_addr(ctx_tcp
->ip
);
520 if (bind(new_s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == -1) {
525 if (listen(new_s
, nb_connection
) == -1) {
533 int modbus_tcp_pi_listen(modbus_t
*ctx
, int nb_connection
)
536 struct addrinfo
*ai_list
;
537 struct addrinfo
*ai_ptr
;
538 struct addrinfo ai_hints
;
542 modbus_tcp_pi_t
*ctx_tcp_pi
;
549 ctx_tcp_pi
= ctx
->backend_data
;
552 if (_modbus_tcp_init_win32() == -1) {
557 if (ctx_tcp_pi
->node
[0] == 0) {
558 node
= NULL
; /* == any */
560 node
= ctx_tcp_pi
->node
;
563 if (ctx_tcp_pi
->service
[0] == 0) {
566 service
= ctx_tcp_pi
->service
;
569 memset(&ai_hints
, 0, sizeof (ai_hints
));
570 /* If node is not NULL, than the AI_PASSIVE flag is ignored. */
571 ai_hints
.ai_flags
|= AI_PASSIVE
;
573 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
575 ai_hints
.ai_family
= AF_UNSPEC
;
576 ai_hints
.ai_socktype
= SOCK_STREAM
;
577 ai_hints
.ai_addr
= NULL
;
578 ai_hints
.ai_canonname
= NULL
;
579 ai_hints
.ai_next
= NULL
;
582 rc
= getaddrinfo(node
, service
, &ai_hints
, &ai_list
);
585 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
587 errno
= ECONNREFUSED
;
592 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
595 s
= socket(ai_ptr
->ai_family
, ai_ptr
->ai_socktype
,
596 ai_ptr
->ai_protocol
);
604 rc
= setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
605 (void *)&enable
, sizeof (enable
));
609 perror("setsockopt");
615 rc
= bind(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
);
624 rc
= listen(s
, nb_connection
);
636 freeaddrinfo(ai_list
);
645 int modbus_tcp_accept(modbus_t
*ctx
, int *s
)
647 struct sockaddr_in addr
;
655 addrlen
= sizeof(addr
);
657 /* Inherit socket flags and use accept4 call */
658 ctx
->s
= accept4(*s
, (struct sockaddr
*)&addr
, &addrlen
, SOCK_CLOEXEC
);
660 ctx
->s
= accept(*s
, (struct sockaddr
*)&addr
, &addrlen
);
670 printf("The client connection from %s is accepted\n",
671 inet_ntoa(addr
.sin_addr
));
677 int modbus_tcp_pi_accept(modbus_t
*ctx
, int *s
)
679 struct sockaddr_storage addr
;
687 addrlen
= sizeof(addr
);
689 /* Inherit socket flags and use accept4 call */
690 ctx
->s
= accept4(*s
, (struct sockaddr
*)&addr
, &addrlen
, SOCK_CLOEXEC
);
692 ctx
->s
= accept(*s
, (struct sockaddr
*)&addr
, &addrlen
);
700 printf("The client connection is accepted.\n");
706 static int _modbus_tcp_select(modbus_t
*ctx
, fd_set
*rset
, struct timeval
*tv
, int length_to_read
)
709 while ((s_rc
= select(ctx
->s
+1, rset
, NULL
, NULL
, tv
)) == -1) {
710 if (errno
== EINTR
) {
712 fprintf(stderr
, "A non blocked signal was caught\n");
714 /* Necessary after an error */
716 FD_SET(ctx
->s
, rset
);
730 static void _modbus_tcp_free(modbus_t
*ctx
) {
731 free(ctx
->backend_data
);
735 const modbus_backend_t _modbus_tcp_backend
= {
736 _MODBUS_BACKEND_TYPE_TCP
,
737 _MODBUS_TCP_HEADER_LENGTH
,
738 _MODBUS_TCP_CHECKSUM_LENGTH
,
739 MODBUS_TCP_MAX_ADU_LENGTH
,
741 _modbus_tcp_build_request_basis
,
742 _modbus_tcp_build_response_basis
,
743 _modbus_tcp_prepare_response_tid
,
744 _modbus_tcp_send_msg_pre
,
748 _modbus_tcp_check_integrity
,
749 _modbus_tcp_pre_check_confirmation
,
758 const modbus_backend_t _modbus_tcp_pi_backend
= {
759 _MODBUS_BACKEND_TYPE_TCP
,
760 _MODBUS_TCP_HEADER_LENGTH
,
761 _MODBUS_TCP_CHECKSUM_LENGTH
,
762 MODBUS_TCP_MAX_ADU_LENGTH
,
764 _modbus_tcp_build_request_basis
,
765 _modbus_tcp_build_response_basis
,
766 _modbus_tcp_prepare_response_tid
,
767 _modbus_tcp_send_msg_pre
,
771 _modbus_tcp_check_integrity
,
772 _modbus_tcp_pre_check_confirmation
,
773 _modbus_tcp_pi_connect
,
780 modbus_t
* modbus_new_tcp(const char *ip
, int port
)
783 modbus_tcp_t
*ctx_tcp
;
788 /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
789 handler for SIGPIPE. */
792 sa
.sa_handler
= SIG_IGN
;
793 if (sigaction(SIGPIPE
, &sa
, NULL
) < 0) {
794 /* The debug flag can't be set here... */
795 fprintf(stderr
, "Coud not install SIGPIPE handler.\n");
800 ctx
= (modbus_t
*)malloc(sizeof(modbus_t
));
801 _modbus_init_common(ctx
);
803 /* Could be changed after to reach a remote serial Modbus device */
804 ctx
->slave
= MODBUS_TCP_SLAVE
;
806 ctx
->backend
= &_modbus_tcp_backend
;
808 ctx
->backend_data
= (modbus_tcp_t
*)malloc(sizeof(modbus_tcp_t
));
809 ctx_tcp
= (modbus_tcp_t
*)ctx
->backend_data
;
812 dest_size
= sizeof(char) * 16;
813 ret_size
= strlcpy(ctx_tcp
->ip
, ip
, dest_size
);
815 fprintf(stderr
, "The IP string is empty\n");
821 if (ret_size
>= dest_size
) {
822 fprintf(stderr
, "The IP string has been truncated\n");
828 ctx_tcp
->ip
[0] = '0';
830 ctx_tcp
->port
= port
;
837 modbus_t
* modbus_new_tcp_pi(const char *node
, const char *service
)
840 modbus_tcp_pi_t
*ctx_tcp_pi
;
844 ctx
= (modbus_t
*)malloc(sizeof(modbus_t
));
845 _modbus_init_common(ctx
);
847 /* Could be changed after to reach a remote serial Modbus device */
848 ctx
->slave
= MODBUS_TCP_SLAVE
;
850 ctx
->backend
= &_modbus_tcp_pi_backend
;
852 ctx
->backend_data
= (modbus_tcp_pi_t
*)malloc(sizeof(modbus_tcp_pi_t
));
853 ctx_tcp_pi
= (modbus_tcp_pi_t
*)ctx
->backend_data
;
856 /* The node argument can be empty to indicate any hosts */
857 ctx_tcp_pi
->node
[0] = '0';
859 dest_size
= sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH
;
860 ret_size
= strlcpy(ctx_tcp_pi
->node
, node
, dest_size
);
862 fprintf(stderr
, "The node string is empty\n");
868 if (ret_size
>= dest_size
) {
869 fprintf(stderr
, "The node string has been truncated\n");
876 if (service
!= NULL
) {
877 dest_size
= sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH
;
878 ret_size
= strlcpy(ctx_tcp_pi
->service
, service
, dest_size
);
880 /* Empty service is not allowed, error catched below. */
885 fprintf(stderr
, "The service string is empty\n");
891 if (ret_size
>= dest_size
) {
892 fprintf(stderr
, "The service string has been truncated\n");
898 ctx_tcp_pi
->t_id
= 0;