2 EIBD eib bus access and management daemon
3 Copyright (C) 2005-2009 Martin Koegler <mkoegler@auto.tuwien.ac.at>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <sys/socket.h>
26 #ifdef HAVE_LINUX_NETLINK
27 #include <asm/types.h>
28 #include <linux/netlink.h>
29 #include <linux/rtnetlink.h>
31 #ifdef HAVE_WINDOWS_IPHELPER
37 #if HAVE_BSD_SOURCEINFO
39 #include <net/route.h>
43 GetHostIP (struct sockaddr_in
*sock
, const char *Name
)
48 memset (sock
, 0, sizeof (*sock
));
49 h
= gethostbyname (Name
);
52 #ifdef HAVE_SOCKADDR_IN_LEN
53 sock
->sin_len
= sizeof (*sock
);
55 sock
->sin_family
= h
->h_addrtype
;
56 sock
->sin_addr
.s_addr
= (*((unsigned long *) h
->h_addr_list
[0]));
60 #ifdef HAVE_LINUX_NETLINK
69 GetSourceAddress (const struct sockaddr_in
*dest
, struct sockaddr_in
*src
)
75 memset (&req
, 0, sizeof (req
));
76 memset (src
, 0, sizeof (*src
));
77 s
= socket (PF_NETLINK
, SOCK_DGRAM
, NETLINK_ROUTE
);
81 NLMSG_SPACE (sizeof (req
.r
)) + RTA_LENGTH (sizeof (*dest
));
82 req
.n
.nlmsg_flags
= NLM_F_REQUEST
;
83 req
.n
.nlmsg_type
= RTM_GETROUTE
;
84 req
.r
.rtm_family
= AF_INET
;
85 req
.r
.rtm_dst_len
= 32;
86 a
= (rtattr
*) ((char *) &req
+ NLMSG_SPACE (sizeof (req
.r
)));
87 a
->rta_type
= RTA_DST
;
88 a
->rta_len
= RTA_LENGTH (sizeof (dest
->sin_addr
.s_addr
));
89 memcpy (RTA_DATA (a
), &dest
->sin_addr
.s_addr
,
90 sizeof (dest
->sin_addr
.s_addr
));
91 if (write (s
, &req
, req
.n
.nlmsg_len
) < 0)
93 if (read (s
, &req
, sizeof (req
)) < 0)
96 if (req
.n
.nlmsg_type
== NLMSG_ERROR
)
98 l
= ((struct nlmsghdr
*) &req
)->nlmsg_len
;
101 if (a
->rta_type
== RTA_PREFSRC
102 && RTA_PAYLOAD (a
) == sizeof (src
->sin_addr
.s_addr
))
104 src
->sin_family
= AF_INET
;
105 memcpy (&src
->sin_addr
.s_addr
, RTA_DATA (a
), RTA_PAYLOAD (a
));
114 #ifdef HAVE_WINDOWS_IPHELPER
116 GetSourceAddress (const struct sockaddr_in
*dest
, struct sockaddr_in
*src
)
119 PMIB_IPADDRTABLE tab
;
122 memset (src
, 0, sizeof (*src
));
123 if (GetBestInterface (dest
->sin_addr
.s_addr
, &d
) != NO_ERROR
)
126 tab
= (MIB_IPADDRTABLE
*) malloc (sizeof (MIB_IPADDRTABLE
));
129 if (GetIpAddrTable (tab
, &s
, 0) == ERROR_INSUFFICIENT_BUFFER
)
131 tab
= (MIB_IPADDRTABLE
*) realloc (tab
, s
);
135 if (GetIpAddrTable (tab
, &s
, 0) != NO_ERROR
)
141 for (int i
= 0; i
< tab
->dwNumEntries
; i
++)
142 if (tab
->table
[i
].dwIndex
== d
)
144 src
->sin_family
= AF_INET
;
145 src
->sin_addr
.s_addr
= tab
->table
[i
].dwAddr
;
154 #if HAVE_BSD_SOURCEINFO
157 struct rt_msghdr hdr
;
163 SA_SIZE (struct sockaddr
*sa
)
165 int align
= sizeof (long);
166 int len
= (sa
->sa_len
? sa
->sa_len
: align
);
167 if (len
& (align
- 1))
169 len
+= align
- (len
& (align
- 1));
176 GetSourceAddress (const struct sockaddr_in
*dest
, struct sockaddr_in
*src
)
181 memset (&req
, 0, sizeof (req
));
182 memset (src
, 0, sizeof (*src
));
183 s
= socket (PF_ROUTE
, SOCK_RAW
, 0);
186 req
.hdr
.rtm_msglen
= sizeof (req
) + sizeof (*dest
);
187 req
.hdr
.rtm_version
= RTM_VERSION
;
188 req
.hdr
.rtm_flags
= RTF_UP
;
189 req
.hdr
.rtm_type
= RTM_GET
;
190 req
.hdr
.rtm_addrs
= RTA_DST
| RTA_IFP
;
191 memcpy (cp
, dest
, sizeof (*dest
));
192 if (write (s
, (char *) &req
, req
.hdr
.rtm_msglen
) < 0)
194 if (read (s
, (char *) &req
, sizeof (req
)) < 0)
198 cp
= (char *) (&req
.hdr
+ 1);
199 for (i
= 1; i
; i
<<= 1)
200 if (i
& req
.hdr
.rtm_addrs
)
202 struct sockaddr
*sa
= (struct sockaddr
*) cp
;
205 src
->sin_len
= sizeof (*src
);
206 src
->sin_family
= AF_INET
;
207 src
->sin_addr
.s_addr
=
208 ((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
;
217 EIBNetIPPacket::EIBNetIPPacket ()
220 memset (&src
, 0, sizeof (src
));
224 EIBNetIPPacket::fromPacket (const CArray
& c
, const struct sockaddr_in src
)
230 if (c
[0] != 0x6 || c
[1] != 0x10)
232 len
= (c
[4] << 8) | c
[5];
235 p
= new EIBNetIPPacket
;
236 p
->service
= (c
[2] << 8) | c
[3];
237 p
->data
.set (c
.array () + 6, len
- 6);
243 EIBNetIPPacket::ToPacket ()
247 c
.resize (6 + data ());
250 c
[2] = (service
>> 8) & 0xff;
251 c
[3] = (service
) & 0xff;
252 c
[4] = ((data () + 6) >> 8) & 0xff;
253 c
[5] = ((data () + 6)) & 0xff;
259 IPtoEIBNetIP (const struct sockaddr_in
* a
)
265 buf
[2] = (ntohl (a
->sin_addr
.s_addr
) >> 24) & 0xff;
266 buf
[3] = (ntohl (a
->sin_addr
.s_addr
) >> 16) & 0xff;
267 buf
[4] = (ntohl (a
->sin_addr
.s_addr
) >> 8) & 0xff;
268 buf
[5] = (ntohl (a
->sin_addr
.s_addr
) >> 0) & 0xff;
269 buf
[6] = (ntohs (a
->sin_port
) >> 8) & 0xff;
270 buf
[7] = (ntohs (a
->sin_port
) >> 0) & 0xff;
275 EIBnettoIP (const CArray
& buf
, struct sockaddr_in
*a
)
278 memset (a
, 0, sizeof (*a
));
279 if (buf
[0] != 0x8 || buf
[1] != 0x1)
281 ip
= (buf
[2] << 24) | (buf
[3] << 16) | (buf
[4] << 8) | (buf
[5]);
282 port
= (buf
[6] << 8) | (buf
[7]);
283 #ifdef HAVE_SOCKADDR_IN_LEN
284 a
->sin_len
= sizeof (*a
);
286 a
->sin_family
= AF_INET
;
287 a
->sin_port
= htons (port
);
288 a
->sin_addr
.s_addr
= htonl (ip
);
292 EIBNetIPSocket::EIBNetIPSocket (struct sockaddr_in bindaddr
, bool reuseaddr
,
297 TRACEPRINTF (t
, 0, this, "Open");
299 pth_sem_init (&insignal
);
300 pth_sem_init (&outsignal
);
301 getwait
= pth_event (PTH_EVENT_SEM
, &outsignal
);
302 memset (&maddr
, 0, sizeof (maddr
));
303 memset (&sendaddr
, 0, sizeof (sendaddr
));
304 memset (&recvaddr
, 0, sizeof (recvaddr
));
307 fd
= socket (AF_INET
, SOCK_DGRAM
, 0);
314 if (setsockopt (fd
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof (i
)) == -1)
321 if (bind (fd
, (struct sockaddr
*) &bindaddr
, sizeof (bindaddr
)) == -1)
329 TRACEPRINTF (t
, 0, this, "Openend");
332 EIBNetIPSocket::~EIBNetIPSocket ()
334 TRACEPRINTF (t
, 0, this, "Close");
336 pth_event_free (getwait
, PTH_FREE_THIS
);
340 setsockopt (fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &maddr
,
347 EIBNetIPSocket::init ()
353 EIBNetIPSocket::SetMulticast (struct ip_mreq multicastaddr
)
357 maddr
= multicastaddr
;
358 if (setsockopt (fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &maddr
, sizeof (maddr
))
366 EIBNetIPSocket::Send (EIBNetIPPacket p
)
368 struct _EIBNetIP_Send s
;
369 t
->TracePacket (1, this, "Send", p
.data
);
373 pth_sem_inc (&insignal
, 1);
377 EIBNetIPSocket::Get (pth_event_t stop
)
380 pth_event_concat (getwait
, stop
, NULL
);
385 pth_event_isolate (getwait
);
387 if (pth_event_status (getwait
) == PTH_STATUS_OCCURRED
)
389 pth_sem_dec (&outsignal
);
390 t
->TracePacket (1, this, "Recv", outqueue
.top ().data
);
391 return new EIBNetIPPacket (outqueue
.get ());
398 EIBNetIPSocket::Run (pth_sem_t
* stop1
)
405 pth_event_t stop
= pth_event (PTH_EVENT_SEM
, stop1
);
406 pth_event_t input
= pth_event (PTH_EVENT_SEM
, &insignal
);
407 while (pth_event_status (stop
) != PTH_STATUS_OCCURRED
)
409 pth_event_concat (stop
, input
, NULL
);
411 memset (&r
, 0, sizeof (r
));
413 pth_recvfrom_ev (fd
, buf
, sizeof (buf
), 0, (struct sockaddr
*) &r
,
415 if (i
> 0 && rl
== sizeof (r
))
417 if (recvall
== 1 || !memcmp (&r
, &recvaddr
, sizeof (r
)) ||
418 (recvall
== 2 && memcmp (&r
, &localaddr
, sizeof (r
))))
420 t
->TracePacket (0, this, "Recv", i
, buf
);
422 EIBNetIPPacket::fromPacket (CArray (buf
, i
), r
);
427 pth_sem_inc (&outsignal
, 1);
431 pth_event_isolate (stop
);
432 if (!inqueue
.isempty ())
434 const struct _EIBNetIP_Send s
= inqueue
.top ();
435 CArray p
= s
.data
.ToPacket ();
436 t
->TracePacket (0, this, "Send", p
);
438 pth_sendto_ev (fd
, p
.array (), p (), 0,
439 (const struct sockaddr
*) &s
.addr
, sizeof (s
.addr
),
443 pth_sem_dec (&insignal
);
451 t
->TracePacket (0, this, "Drop EIBnetSocket", p
);
452 pth_sem_dec (&insignal
);
458 pth_event_free (stop
, PTH_FREE_THIS
);
459 pth_event_free (input
, PTH_FREE_THIS
);
462 EIBnet_ConnectRequest::EIBnet_ConnectRequest ()
464 memset (&caddr
, 0, sizeof (caddr
));
465 memset (&daddr
, 0, sizeof (daddr
));
468 EIBNetIPPacket
EIBnet_ConnectRequest::ToPacket ()CONST
475 ca
= IPtoEIBNetIP (&caddr
);
476 da
= IPtoEIBNetIP (&daddr
);
477 p
.service
= CONNECTION_REQUEST
;
478 p
.data
.resize (ca () + da () + 1 + CRI ());
479 p
.data
.setpart (ca
, 0);
480 p
.data
.setpart (da
, ca ());
481 p
.data
[ca () + da ()] = CRI () + 1;
482 p
.data
.setpart (CRI
, ca () + da () + 1);
487 parseEIBnet_ConnectRequest (const EIBNetIPPacket
& p
,
488 EIBnet_ConnectRequest
& r
)
490 if (p
.service
!= CONNECTION_REQUEST
)
494 if (EIBnettoIP (CArray (p
.data
.array (), 8), &r
.caddr
))
496 if (EIBnettoIP (CArray (p
.data
.array () + 8, 8), &r
.daddr
))
498 if (p
.data () - 16 != p
.data
[16])
500 r
.CRI
= CArray (p
.data
.array () + 17, p
.data () - 17);
504 EIBnet_ConnectResponse::EIBnet_ConnectResponse ()
506 memset (&daddr
, 0, sizeof (daddr
));
511 EIBNetIPPacket
EIBnet_ConnectResponse::ToPacket ()CONST
516 da
= IPtoEIBNetIP (&daddr
);
517 p
.service
= CONNECTION_RESPONSE
;
518 p
.data
.resize (da () + CRD () + 3);
521 p
.data
.setpart (da
, 2);
522 p
.data
[da () + 2] = CRD () + 1;
523 p
.data
.setpart (CRD
, da () + 3);
528 parseEIBnet_ConnectResponse (const EIBNetIPPacket
& p
,
529 EIBnet_ConnectResponse
& r
)
531 if (p
.service
!= CONNECTION_RESPONSE
)
535 if (EIBnettoIP (CArray (p
.data
.array () + 2, 8), &r
.daddr
))
537 if (p
.data () - 10 != p
.data
[10])
539 r
.channel
= p
.data
[0];
540 r
.status
= p
.data
[1];
541 r
.CRD
= CArray (p
.data
.array () + 11, p
.data () - 11);
545 EIBnet_ConnectionStateRequest::EIBnet_ConnectionStateRequest ()
547 memset (&caddr
, 0, sizeof (caddr
));
551 EIBNetIPPacket
EIBnet_ConnectionStateRequest::ToPacket ()CONST
556 ca
= IPtoEIBNetIP (&caddr
);
557 p
.service
= CONNECTIONSTATE_REQUEST
;
558 p
.data
.resize (ca () + 2);
561 p
.data
.setpart (ca
, 2);
566 parseEIBnet_ConnectionStateRequest (const EIBNetIPPacket
& p
,
567 EIBnet_ConnectionStateRequest
& r
)
569 if (p
.service
!= CONNECTIONSTATE_REQUEST
)
573 if (EIBnettoIP (CArray (p
.data
.array () + 2, 8), &r
.caddr
))
575 r
.channel
= p
.data
[0];
579 EIBnet_ConnectionStateResponse::EIBnet_ConnectionStateResponse ()
585 EIBNetIPPacket
EIBnet_ConnectionStateResponse::ToPacket ()CONST
589 p
.service
= CONNECTIONSTATE_RESPONSE
;
597 parseEIBnet_ConnectionStateResponse (const EIBNetIPPacket
& p
,
598 EIBnet_ConnectionStateResponse
& r
)
600 if (p
.service
!= CONNECTIONSTATE_RESPONSE
)
604 r
.channel
= p
.data
[0];
605 r
.status
= p
.data
[1];
609 EIBnet_DisconnectRequest::EIBnet_DisconnectRequest ()
611 memset (&caddr
, 0, sizeof (caddr
));
615 EIBNetIPPacket
EIBnet_DisconnectRequest::ToPacket ()CONST
620 ca
= IPtoEIBNetIP (&caddr
);
621 p
.service
= DISCONNECT_REQUEST
;
622 p
.data
.resize (ca () + 2);
625 p
.data
.setpart (ca
, 2);
630 parseEIBnet_DisconnectRequest (const EIBNetIPPacket
& p
,
631 EIBnet_DisconnectRequest
& r
)
633 if (p
.service
!= DISCONNECT_REQUEST
)
637 if (EIBnettoIP (CArray (p
.data
.array () + 2, 8), &r
.caddr
))
639 r
.channel
= p
.data
[0];
643 EIBnet_DisconnectResponse::EIBnet_DisconnectResponse ()
649 EIBNetIPPacket
EIBnet_DisconnectResponse::ToPacket ()CONST
653 p
.service
= DISCONNECT_RESPONSE
;
661 parseEIBnet_DisconnectResponse (const EIBNetIPPacket
& p
,
662 EIBnet_DisconnectResponse
& r
)
664 if (p
.service
!= DISCONNECT_RESPONSE
)
668 r
.channel
= p
.data
[0];
669 r
.status
= p
.data
[1];
673 EIBnet_TunnelRequest::EIBnet_TunnelRequest ()
679 EIBNetIPPacket
EIBnet_TunnelRequest::ToPacket ()CONST
683 p
.service
= TUNNEL_REQUEST
;
684 p
.data
.resize (CEMI () + 4);
689 p
.data
.setpart (CEMI
, 4);
694 parseEIBnet_TunnelRequest (const EIBNetIPPacket
& p
, EIBnet_TunnelRequest
& r
)
696 if (p
.service
!= TUNNEL_REQUEST
)
702 r
.channel
= p
.data
[1];
704 r
.CEMI
.set (p
.data
.array () + 4, p
.data () - 4);
708 EIBnet_TunnelACK::EIBnet_TunnelACK ()
715 EIBNetIPPacket
EIBnet_TunnelACK::ToPacket ()CONST
719 p
.service
= TUNNEL_RESPONSE
;
729 parseEIBnet_TunnelACK (const EIBNetIPPacket
& p
, EIBnet_TunnelACK
& r
)
731 if (p
.service
!= TUNNEL_RESPONSE
)
737 r
.channel
= p
.data
[1];
739 r
.status
= p
.data
[3];
743 EIBnet_DescriptionRequest::EIBnet_DescriptionRequest ()
745 memset (&caddr
, 0, sizeof (caddr
));
748 EIBNetIPPacket
EIBnet_DescriptionRequest::ToPacket ()CONST
753 ca
= IPtoEIBNetIP (&caddr
);
754 p
.service
= DESCRIPTION_REQUEST
;
760 parseEIBnet_DescriptionRequest (const EIBNetIPPacket
& p
,
761 EIBnet_DescriptionRequest
& r
)
763 if (p
.service
!= DESCRIPTION_REQUEST
)
767 if (EIBnettoIP (p
.data
, &r
.caddr
))
773 EIBnet_DescriptionResponse::EIBnet_DescriptionResponse ()
779 memset (&serial
, 0, sizeof (serial
));
780 multicastaddr
.s_addr
= 0;
781 memset (&MAC
, 0, sizeof (MAC
));
782 memset (&name
, 0, sizeof (name
));
785 EIBNetIPPacket
EIBnet_DescriptionResponse::ToPacket ()CONST
789 p
.service
= DESCRIPTION_RESPONSE
;
790 p
.data
.resize (56 + services () * 2);
793 p
.data
[2] = KNXmedium
;
794 p
.data
[3] = devicestatus
;
795 p
.data
[4] = (individual_addr
>> 8) & 0xff;
796 p
.data
[5] = (individual_addr
) & 0xff;
797 p
.data
[6] = (installid
>> 8) & 0xff;
798 p
.data
[7] = (installid
) & 0xff;
799 memcpy (p
.data
.array () + 18, &serial
, 6);
800 memcpy (p
.data
.array () + 14, &multicastaddr
, 4);
801 memcpy (p
.data
.array () + 18, &MAC
, 6);
802 memcpy (p
.data
.array () + 24, &name
, 30);
804 p
.data
[54] = services () * 2 + 2;
806 for (int i
= 0; i
< services (); i
++)
808 p
.data
[56 + i
* 2] = services
[i
].family
;
809 p
.data
[57 + i
* 2] = services
[i
].version
;
811 p
.data
.setpart (optional
, 56 + services () * 2);
816 parseEIBnet_DescriptionResponse (const EIBNetIPPacket
& p
,
817 EIBnet_DescriptionResponse
& r
)
819 if (p
.service
!= DESCRIPTION_RESPONSE
)
827 r
.KNXmedium
= p
.data
[2];
828 r
.devicestatus
= p
.data
[3];
829 r
.individual_addr
= (p
.data
[4] << 8) | p
.data
[5];
830 r
.installid
= (p
.data
[6] << 8) | p
.data
[7];
831 memcpy (&r
.serial
, p
.data
.array () + 8, 6);
832 memcpy (&r
.multicastaddr
, p
.data
.array () + 14, 4);
833 memcpy (&r
.MAC
, p
.data
.array () + 18, 6);
834 memcpy (&r
.name
, p
.data
.array () + 24, 30);
840 if (p
.data
[54] + 54 > p
.data ())
842 r
.services
.resize ((p
.data
[54] / 2) - 1);
843 for (int i
= 0; i
< (p
.data
[54] / 2) - 1; i
++)
845 r
.services
[i
].family
= p
.data
[56 + 2 * i
];
846 r
.services
[i
].version
= p
.data
[57 + 2 * i
];
848 r
.optional
.set (p
.data
.array () + p
.data
[54] + 54,
849 p
.data () - p
.data
[54] - 54);
853 EIBnet_SearchRequest::EIBnet_SearchRequest ()
855 memset (&caddr
, 0, sizeof (caddr
));
858 EIBNetIPPacket
EIBnet_SearchRequest::ToPacket ()CONST
863 ca
= IPtoEIBNetIP (&caddr
);
864 p
.service
= SEARCH_REQUEST
;
870 parseEIBnet_SearchRequest (const EIBNetIPPacket
& p
, EIBnet_SearchRequest
& r
)
872 if (p
.service
!= SEARCH_REQUEST
)
876 if (EIBnettoIP (p
.data
, &r
.caddr
))
882 EIBnet_SearchResponse::EIBnet_SearchResponse ()
888 memset (&serial
, 0, sizeof (serial
));
889 multicastaddr
.s_addr
= 0;
890 memset (&MAC
, 0, sizeof (MAC
));
891 memset (&name
, 0, sizeof (name
));
894 EIBNetIPPacket
EIBnet_SearchResponse::ToPacket ()CONST
899 ca
= IPtoEIBNetIP (&caddr
);
900 p
.service
= SEARCH_RESPONSE
;
901 p
.data
.resize (64 + services () * 2);
902 p
.data
.setpart (ca
, 0);
905 p
.data
[10] = KNXmedium
;
906 p
.data
[11] = devicestatus
;
907 p
.data
[12] = (individual_addr
>> 8) & 0xff;
908 p
.data
[13] = (individual_addr
) & 0xff;
909 p
.data
[14] = (installid
>> 8) & 0xff;
910 p
.data
[15] = (installid
) & 0xff;
911 memcpy (p
.data
.array () + 16, &serial
, 6);
912 memcpy (p
.data
.array () + 22, &multicastaddr
, 4);
913 memcpy (p
.data
.array () + 26, &MAC
, 6);
914 memcpy (p
.data
.array () + 32, &name
, 30);
916 p
.data
[62] = services () * 2 + 2;
918 for (int i
= 0; i
< services (); i
++)
920 p
.data
[64 + i
* 2] = services
[i
].family
;
921 p
.data
[65 + i
* 2] = services
[i
].version
;
927 parseEIBnet_SearchResponse (const EIBNetIPPacket
& p
,
928 EIBnet_SearchResponse
& r
)
930 if (p
.service
!= SEARCH_RESPONSE
)
934 if (EIBnettoIP (CArray (p
.data
.array () + 0, 8), &r
.caddr
))
940 r
.KNXmedium
= p
.data
[10];
941 r
.devicestatus
= p
.data
[11];
942 r
.individual_addr
= (p
.data
[12] << 8) | p
.data
[13];
943 r
.installid
= (p
.data
[14] << 8) | p
.data
[15];
944 memcpy (&r
.serial
, p
.data
.array () + 16, 6);
945 memcpy (&r
.multicastaddr
, p
.data
.array () + 22, 4);
946 memcpy (&r
.MAC
, p
.data
.array () + 26, 6);
947 memcpy (&r
.name
, p
.data
.array () + 32, 30);
953 if (p
.data
[62] + 62 > p
.data ())
955 r
.services
.resize ((p
.data
[62] / 2) - 1);
956 for (int i
= 0; i
< (p
.data
[62] / 2) - 1; i
++)
958 r
.services
[i
].family
= p
.data
[64 + 2 * i
];
959 r
.services
[i
].version
= p
.data
[65 + 2 * i
];