1 /*
2  *
3  *  Copyright (C) 1994-2018, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were partly developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *  For further copyrights, see the following paragraphs.
14  *
15  */
16 
17 /*
18           Copyright (C) 1993, 1994, RSNA and Washington University
19 
20           The software and supporting documentation for the Radiological
21           Society of North America (RSNA) 1993, 1994 Digital Imaging and
22           Communications in Medicine (DICOM) Demonstration were developed
23           at the
24                   Electronic Radiology Laboratory
25                   Mallinckrodt Institute of Radiology
26                   Washington University School of Medicine
27                   510 S. Kingshighway Blvd.
28                   St. Louis, MO 63110
29           as part of the 1993, 1994 DICOM Central Test Node project for, and
30           under contract with, the Radiological Society of North America.
31 
32           THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND NEITHER RSNA NOR
33           WASHINGTON UNIVERSITY MAKE ANY WARRANTY ABOUT THE SOFTWARE, ITS
34           PERFORMANCE, ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
35           USE, FREEDOM FROM ANY COMPUTER DISEASES OR ITS CONFORMITY TO ANY
36           SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND PERFORMANCE OF
37           THE SOFTWARE IS WITH THE USER.
38 
39           Copyright of the software and supporting documentation is
40           jointly owned by RSNA and Washington University, and free access
41           is hereby granted as a license to use this software, copy this
42           software and prepare derivative works based upon this software.
43           However, any distribution of this software source code or
44           supporting documentation or derivative works (source code and
45           supporting documentation) must include the three paragraphs of
46           the copyright notice.
47 */
48 
49 /*
50 **          DICOM 93
51 **        Electronic Radiology Laboratory
52 **      Mallinckrodt Institute of Radiology
53 **    Washington University School of Medicine
54 **
55 ** Module Name(s):  DUL_InitializeFSM
56 **      PRV_StateMachine
57 **      fsmDebug
58 **
59 ** Author, Date:  Stephen M. Moore, 15-Apr-93
60 ** Intent:        Define tables and provide functions that implement
61 **                the DICOM Upper Layer (DUL) finite state machine.
62 */
63 
64 
65 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
66 
67 #ifdef HAVE_WINDOWS_H
68 // on Windows, we need Winsock2 for network functions
69 #include <winsock2.h>
70 // and ws2tcpip for socklen_t
71 #include <ws2tcpip.h>
72 #endif
73 
74 #define INCLUDE_CSTDLIB
75 #define INCLUDE_CSTDIO
76 #define INCLUDE_CSTRING
77 #define INCLUDE_CERRNO
78 #define INCLUDE_CSIGNAL
79 #define INCLUDE_CTIME
80 #define INCLUDE_UNISTD
81 #include "dcmtk/ofstd/ofstdinc.h"
82 
83 #ifdef HAVE_SYS_TIME_H
84 #include <sys/time.h>
85 #endif
86 #ifdef HAVE_SYS_TYPES_H
87 #include <sys/types.h>
88 #endif
89 #ifdef HAVE_SYS_SELECT_H
90 #include <sys/select.h>
91 #ifdef HAVE_FCNTL_H
92 #include <fcntl.h>
93 #endif
94 #endif
95 
96 BEGIN_EXTERN_C
97 #ifdef HAVE_NETINET_IN_SYSTM_H
98 #include <netinet/in_systm.h>   /* prerequisite for netinet/in.h on NeXT */
99 #endif
100 #ifdef HAVE_NETINET_IN_H
101 #include <netinet/in.h>         /* prerequisite for netinet/tcp.h on NeXT */
102 #endif
103 #ifdef HAVE_NETINET_TCP_H
104 #include <netinet/tcp.h>        /* for TCP_NODELAY */
105 #endif
106 END_EXTERN_C
107 #ifdef DCMTK_HAVE_POLL
108 #include <poll.h>
109 #endif
110 
111 #include "dcmtk/ofstd/ofstream.h"
112 #include "dcmtk/dcmnet/dicom.h"
113 #include "dcmtk/dcmnet/lst.h"
114 #include "dcmtk/dcmnet/cond.h"
115 #include "dcmtk/dcmnet/dul.h"
116 #include "dulstruc.h"
117 #include "dulpriv.h"
118 #include "dulfsm.h"
119 #include "dcmtk/ofstd/ofbmanip.h"
120 #include "dcmtk/ofstd/ofconsol.h"
121 #include "dcmtk/dcmnet/assoc.h"    /* for ASC_MAXIMUMPDUSIZE */
122 #include "dcmtk/dcmnet/dcmtrans.h"
123 #include "dcmtk/dcmnet/dcmlayer.h"
124 #include "dcmtk/dcmnet/diutil.h"
125 #include "dcmtk/ofstd/ofsockad.h" /* for class OFSockAddr */
126 
127 /* At least Solaris doesn't define this */
128 #ifndef INADDR_NONE
129 #define INADDR_NONE 0xffffffff
130 #endif
131 
132 /* platform independent definition of EINTR */
133 enum
134 {
135 #ifdef HAVE_WINSOCK_H
136     DCMNET_EINTR = WSAEINTR
137 #else
138     DCMNET_EINTR = EINTR
139 #endif
140 };
141 
142 static OFCondition
143 AE_1_TransportConnect(PRIVATE_NETWORKKEY ** network,
144         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
145 static OFCondition
146 AE_2_SendAssociateRQPDU(PRIVATE_NETWORKKEY ** network,
147         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
148 static OFCondition
149 AE_3_AssociateConfirmationAccept(PRIVATE_NETWORKKEY ** network,
150         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
151 static OFCondition
152 AE_4_AssociateConfirmationReject(PRIVATE_NETWORKKEY ** network,
153         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
154 static OFCondition
155 AE_5_TransportConnectResponse(PRIVATE_NETWORKKEY ** network,
156         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
157 static OFCondition
158 AE_6_ExamineAssociateRequest(PRIVATE_NETWORKKEY ** network,
159         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
160 static OFCondition
161 AE_7_SendAssociateAC(PRIVATE_NETWORKKEY ** network,
162         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
163 static OFCondition
164 AE_8_SendAssociateRJ(PRIVATE_NETWORKKEY ** network,
165         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
166 
167 static OFCondition
168 DT_1_SendPData(PRIVATE_NETWORKKEY ** network,
169          PRIVATE_ASSOCIATIONKEY ** associatin, int nextState, void *params);
170 static OFCondition
171 DT_2_IndicatePData(PRIVATE_NETWORKKEY ** network,
172         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
173 
174 static OFCondition
175 AA_1_SendAAbort(PRIVATE_NETWORKKEY ** network,
176          PRIVATE_ASSOCIATIONKEY ** associatin, int nextState, void *params);
177 static OFCondition
178 AA_2_CloseTransport(PRIVATE_NETWORKKEY ** network,
179         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
180 static OFCondition
181 AA_2_CloseTimeout(PRIVATE_NETWORKKEY ** network,
182         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
183 static OFCondition
184 AA_3_IndicatePeerAborted(PRIVATE_NETWORKKEY ** network,
185         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
186 static OFCondition
187 AA_4_IndicateAPAbort(PRIVATE_NETWORKKEY ** network,
188         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
189 static OFCondition
190 AA_5_StopARTIMtimer(PRIVATE_NETWORKKEY ** network,
191         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
192 static OFCondition
193 AA_6_IgnorePDU(PRIVATE_NETWORKKEY ** network,
194         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
195 static OFCondition
196 AA_7_State13SendAbort(PRIVATE_NETWORKKEY ** network,
197         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
198 static OFCondition
199 AA_8_UnrecognizedPDUSendAbort(PRIVATE_NETWORKKEY ** network,
200         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
201 
202 static OFCondition
203 AR_1_SendReleaseRQ(PRIVATE_NETWORKKEY ** network,
204         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
205 static OFCondition
206 AR_2_IndicateRelease(PRIVATE_NETWORKKEY ** network,
207         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
208 static OFCondition
209 AR_3_ConfirmRelease(PRIVATE_NETWORKKEY ** network,
210         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
211 static OFCondition
212 AR_4_SendReleaseRP(PRIVATE_NETWORKKEY ** network,
213         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
214 static OFCondition
215 AR_5_StopARTIMtimer(PRIVATE_NETWORKKEY ** network,
216         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
217 static OFCondition
218 AR_6_IndicatePData(PRIVATE_NETWORKKEY ** network,
219         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
220 static OFCondition
221 AR_7_SendPDATA(PRIVATE_NETWORKKEY ** network,
222         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
223 static OFCondition
224 AR_8_IndicateARelease(PRIVATE_NETWORKKEY ** network,
225         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
226 static OFCondition
227 AR_9_SendAReleaseRP(PRIVATE_NETWORKKEY ** network,
228         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
229 static OFCondition
230 AR_10_ConfirmRelease(PRIVATE_NETWORKKEY ** network,
231         PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params);
232 
233 static OFCondition
234 requestAssociationTCP(PRIVATE_NETWORKKEY ** network,
235                       DUL_ASSOCIATESERVICEPARAMETERS * params,
236                       PRIVATE_ASSOCIATIONKEY ** association);
237 static OFCondition
238 sendAssociationRQTCP(PRIVATE_NETWORKKEY ** network,
239                      DUL_ASSOCIATESERVICEPARAMETERS * params,
240                      PRIVATE_ASSOCIATIONKEY ** association);
241 static OFCondition
242 sendAssociationACTCP(PRIVATE_NETWORKKEY ** network,
243                      DUL_ASSOCIATESERVICEPARAMETERS * params,
244                      PRIVATE_ASSOCIATIONKEY ** association);
245 static OFCondition
246 sendAssociationRJTCP(PRIVATE_NETWORKKEY ** network,
247         DUL_ABORTITEMS * abortItems, PRIVATE_ASSOCIATIONKEY ** association);
248 static OFCondition
249 sendAbortTCP(DUL_ABORTITEMS * abortItems,
250              PRIVATE_ASSOCIATIONKEY ** association);
251 static OFCondition sendReleaseRQTCP(PRIVATE_ASSOCIATIONKEY ** association);
252 static OFCondition sendReleaseRPTCP(PRIVATE_ASSOCIATIONKEY ** association);
253 static OFCondition
254 sendPDataTCP(PRIVATE_ASSOCIATIONKEY ** association,
255              DUL_PDVLIST * pdvList);
256 static OFCondition
257 writeDataPDU(PRIVATE_ASSOCIATIONKEY ** association,
258              DUL_DATAPDU * pdu);
259 static void clearPDUCache(PRIVATE_ASSOCIATIONKEY ** association);
260 static void closeTransport(PRIVATE_ASSOCIATIONKEY ** association);
261 static void closeTransportTCP(PRIVATE_ASSOCIATIONKEY ** association);
262 static OFCondition
263 readPDUHead(PRIVATE_ASSOCIATIONKEY ** association,
264             unsigned char *buffer, unsigned long maxlength,
265             DUL_BLOCKOPTIONS block, int timeout,
266             unsigned char *PDUtype, unsigned char *PDUreserved,
267             unsigned long *PDULength);
268 static OFCondition
269 readPDU(PRIVATE_ASSOCIATIONKEY ** association, DUL_BLOCKOPTIONS block,
270         int timeout, unsigned char **buffer,
271         unsigned char *pduType, unsigned char *pduReserved,
272         unsigned long *pduLength);
273 static OFCondition
274 readPDUBody(PRIVATE_ASSOCIATIONKEY ** association,
275             DUL_BLOCKOPTIONS block, int timeout,
276             unsigned char *buffer, unsigned long maxLength,
277             unsigned char *pduType, unsigned char *pduReserved,
278             unsigned long *pduLength);
279 static OFCondition
280 readPDUHeadTCP(PRIVATE_ASSOCIATIONKEY ** association,
281                unsigned char *buffer, unsigned long maxLength,
282                DUL_BLOCKOPTIONS block, int timeout,
283                unsigned char *PDUtype, unsigned char *PDUreserved,
284                unsigned long *PDULength);
285 static OFCondition
286 readPDUBodyTCP(PRIVATE_ASSOCIATIONKEY ** association,
287                DUL_BLOCKOPTIONS block, int timeout,
288                unsigned char *buffer, unsigned long maxLength,
289                unsigned char *pduType, unsigned char *pduReserved,
290                unsigned long *pduLength);
291 static OFCondition
292 defragmentTCP(DcmTransportConnection *connection, DUL_BLOCKOPTIONS block, time_t timerStart,
293               int timeout, void *b, unsigned long l, unsigned long *rtnLen);
294 
295 static OFString dump_pdu(const char *type, void *buffer, unsigned long length);
296 
297 #ifdef _WIN32
298 static void setTCPBufferLength(SOCKET sock);
299 #else
300 static void setTCPBufferLength(int sock);
301 #endif
302 
303 OFCondition
304 translatePresentationContextList(LST_HEAD ** internalList,
305                                  LST_HEAD ** SCUSCPRoleList,
306                                  LST_HEAD ** userContextList);
307 DUL_PRESENTATIONCONTEXT *
308 findPresentationCtx(LST_HEAD ** lst, DUL_PRESENTATIONCONTEXTID contextID);
309 
310 PRV_SCUSCPROLE *
311 findSCUSCPRole(LST_HEAD ** lst, char *abstractSyntax);
312 
313 void destroyPresentationContextList(LST_HEAD ** l);
314 void destroyUserInformationLists(DUL_USERINFO * userInfo);
315 
316 static volatile FSM_Event_Description Event_Table[] = {
317     {A_ASSOCIATE_REQ_LOCAL_USER, "A-ASSOCIATE request (local user)"},
318     {TRANS_CONN_CONFIRM_LOCAL_USER, "Transport conn confirmation (local)"},
319     {A_ASSOCIATE_AC_PDU_RCV, "A-ASSOCIATE-AC PDU (on transport)"},
320     {A_ASSOCIATE_RJ_PDU_RCV, "A-ASSOCIATE-RJ PDU (on transport)"},
321     {TRANS_CONN_INDICATION, "Transport connection indication"},
322     {A_ASSOCIATE_RQ_PDU_RCV, "A-ASSOCIATE-RQ PDU (on transport)"},
323     {A_ASSOCIATE_RESPONSE_ACCEPT, "A-ASSOCIATE resp prim (accept)"},
324     {A_ASSOCIATE_RESPONSE_REJECT, "A-ASSOCIATE resp prim (reject)"},
325     {P_DATA_REQ, "P-DATA request primitive"},
326     {P_DATA_TF_PDU_RCV, "P-DATA-TF PDU (on transport)"},
327     {A_RELEASE_REQ, "A-RELEASE request primitive"},
328     {A_RELEASE_RQ_PDU_RCV, "A-RELEASE-RQ PDU (on transport)"},
329     {A_RELEASE_RP_PDU_RCV, "A-RELEASE-RP PDU (on transport)"},
330     {A_RELEASE_RESP, "A-RELEASE response primitive"},
331     {A_ABORT_REQ, "A-ABORT request primitive"},
332     {A_ABORT_PDU_RCV, "A-ABORT PDU (on transport)"},
333     {TRANS_CONN_CLOSED, "Transport connection closed"},
334     {ARTIM_TIMER_EXPIRED, "ARTIM timer expired (rej/rel)"},
335     {INVALID_PDU, "Unrecognized/invalid PDU"}
336 };
337 
338 static volatile FSM_FUNCTION FSM_FunctionTable[] = {
339     {AE_1, AE_1_TransportConnect, "AE 1 Transport Connect"},
340     {AE_2, AE_2_SendAssociateRQPDU, "AE 2 Send Associate RQ PDU"},
341     {AE_3, AE_3_AssociateConfirmationAccept, "AE 3 Associate Confirmation Accept"},
342     {AE_4, AE_4_AssociateConfirmationReject, "AE 4 Associate Confirmation Reject"},
343     {AE_5, AE_5_TransportConnectResponse, "AE 5 Transport Connect Response"},
344     {AE_6, AE_6_ExamineAssociateRequest, "AE 6 Examine Associate Request"},
345     {AE_7, AE_7_SendAssociateAC, "AE 7 Send Associate AC"},
346     {AE_8, AE_8_SendAssociateRJ, "AE 8 Send Associate RJ"},
347 
348     {DT_1, DT_1_SendPData, "DT 1 Send P DATA PDU"},
349     {DT_2, DT_2_IndicatePData, "DT 2 Indicate P DATA PDU Received"},
350 
351     {AA_1, AA_1_SendAAbort, "AA 1 Send A ABORT PDU"},
352     {AA_2, AA_2_CloseTransport, "AA 2 Close Transport"},
353     {AA_2T, AA_2_CloseTimeout, "AA 2 Close Transport (Read Timeout)"},
354     {AA_3, AA_3_IndicatePeerAborted, "AA 3 Indicate Peer Aborted"},
355     {AA_4, AA_4_IndicateAPAbort, "AA 4 Indicate AP Abort"},
356     {AA_5, AA_5_StopARTIMtimer, "AA 5 Stop ARTIM timer"},
357     {AA_6, AA_6_IgnorePDU, "AA 6 Ignore PDU"},
358     {AA_7, AA_7_State13SendAbort, "AA 7 State 13 Send Abort"},
359     {AA_8, AA_8_UnrecognizedPDUSendAbort, "AA 8 Unrecognized PDU Send Abort"},
360 
361     {AR_1, AR_1_SendReleaseRQ, "AR 1 Send Release RQ"},
362     {AR_2, AR_2_IndicateRelease, "AR 2 Indicate Release"},
363     {AR_3, AR_3_ConfirmRelease, "AR 3 Confirm Release"},
364     {AR_4, AR_4_SendReleaseRP, "AR 4 Send Release RP"},
365     {AR_5, AR_5_StopARTIMtimer, "AR 5 Stop ARTIM timer"},
366     {AR_6, AR_6_IndicatePData, "AR 6 Indicate P DATA PDU"},
367     {AR_7, AR_7_SendPDATA, "AR 7 Send P DATA PDU"},
368     {AR_8, AR_8_IndicateARelease, "AR 8 Indicate A RELEASE"},
369     {AR_9, AR_9_SendAReleaseRP, "AR 9 Send A RELEASE RP"},
370     {AR_10, AR_10_ConfirmRelease, "AR 10 Confirm Release"}
371 };
372 
373 static volatile FSM_ENTRY StateTable[DUL_NUMBER_OF_EVENTS][DUL_NUMBER_OF_STATES] = {
374     {
375         // EVENT,                    STATE,  ACTION,   NEXT_STATE
376         {A_ASSOCIATE_REQ_LOCAL_USER, STATE1, AE_1, STATE4, "", "", NULL},
377         {A_ASSOCIATE_REQ_LOCAL_USER, STATE2, NOACTION, NOSTATE, "", "", NULL},
378         {A_ASSOCIATE_REQ_LOCAL_USER, STATE3, NOACTION, NOSTATE, "", "", NULL},
379         {A_ASSOCIATE_REQ_LOCAL_USER, STATE4, NOACTION, NOSTATE, "", "", NULL},
380         {A_ASSOCIATE_REQ_LOCAL_USER, STATE5, NOACTION, NOSTATE, "", "", NULL},
381         {A_ASSOCIATE_REQ_LOCAL_USER, STATE6, NOACTION, NOSTATE, "", "", NULL},
382         {A_ASSOCIATE_REQ_LOCAL_USER, STATE7, NOACTION, NOSTATE, "", "", NULL},
383         {A_ASSOCIATE_REQ_LOCAL_USER, STATE8, NOACTION, NOSTATE, "", "", NULL},
384         {A_ASSOCIATE_REQ_LOCAL_USER, STATE9, NOACTION, NOSTATE, "", "", NULL},
385         {A_ASSOCIATE_REQ_LOCAL_USER, STATE10, NOACTION, NOSTATE, "", "", NULL},
386         {A_ASSOCIATE_REQ_LOCAL_USER, STATE11, NOACTION, NOSTATE, "", "", NULL},
387         {A_ASSOCIATE_REQ_LOCAL_USER, STATE12, NOACTION, NOSTATE, "", "", NULL},
388         {A_ASSOCIATE_REQ_LOCAL_USER, STATE13, NOACTION, NOSTATE, "", "", NULL}},
389 
390     {
391         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE1, NOACTION, NOSTATE, "", "", NULL},
392         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE2, NOACTION, NOSTATE, "", "", NULL},
393         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE3, NOACTION, NOSTATE, "", "", NULL},
394         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE4, AE_2, STATE5, "", "", NULL},
395         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE5, NOACTION, NOSTATE, "", "", NULL},
396         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE6, NOACTION, NOSTATE, "", "", NULL},
397         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE7, NOACTION, NOSTATE, "", "", NULL},
398         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE8, NOACTION, NOSTATE, "", "", NULL},
399         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE9, NOACTION, NOSTATE, "", "", NULL},
400         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE10, NOACTION, NOSTATE, "", "", NULL},
401         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE11, NOACTION, NOSTATE, "", "", NULL},
402         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE12, NOACTION, NOSTATE, "", "", NULL},
403         {TRANS_CONN_CONFIRM_LOCAL_USER, STATE13, NOACTION, NOSTATE, "", "", NULL}},
404 
405     {
406         {A_ASSOCIATE_AC_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
407         {A_ASSOCIATE_AC_PDU_RCV, STATE2, AA_1, STATE13, "", "", NULL},
408         {A_ASSOCIATE_AC_PDU_RCV, STATE3, AA_8, STATE13, "", "", NULL},
409         {A_ASSOCIATE_AC_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
410         {A_ASSOCIATE_AC_PDU_RCV, STATE5, AE_3, STATE6, "", "", NULL},
411         {A_ASSOCIATE_AC_PDU_RCV, STATE6, AA_8, STATE13, "", "", NULL},
412         {A_ASSOCIATE_AC_PDU_RCV, STATE7, AA_8, STATE13, "", "", NULL},
413         {A_ASSOCIATE_AC_PDU_RCV, STATE8, AA_8, STATE13, "", "", NULL},
414         {A_ASSOCIATE_AC_PDU_RCV, STATE9, AA_8, STATE13, "", "", NULL},
415         {A_ASSOCIATE_AC_PDU_RCV, STATE10, AA_8, STATE13, "", "", NULL},
416         {A_ASSOCIATE_AC_PDU_RCV, STATE11, AA_8, STATE13, "", "", NULL},
417         {A_ASSOCIATE_AC_PDU_RCV, STATE12, AA_8, STATE13, "", "", NULL},
418         {A_ASSOCIATE_AC_PDU_RCV, STATE13, AA_6, STATE13, "", "", NULL}},
419 
420     {
421         {A_ASSOCIATE_RJ_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
422         {A_ASSOCIATE_RJ_PDU_RCV, STATE2, AA_1, STATE13, "", "", NULL},
423         {A_ASSOCIATE_RJ_PDU_RCV, STATE3, AA_8, STATE13, "", "", NULL},
424         {A_ASSOCIATE_RJ_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
425         {A_ASSOCIATE_RJ_PDU_RCV, STATE5, AE_4, STATE1, "", "", NULL},
426         {A_ASSOCIATE_RJ_PDU_RCV, STATE6, AA_8, STATE13, "", "", NULL},
427         {A_ASSOCIATE_RJ_PDU_RCV, STATE7, AA_8, STATE13, "", "", NULL},
428         {A_ASSOCIATE_RJ_PDU_RCV, STATE8, AA_8, STATE13, "", "", NULL},
429         {A_ASSOCIATE_RJ_PDU_RCV, STATE9, AA_8, STATE13, "", "", NULL},
430         {A_ASSOCIATE_RJ_PDU_RCV, STATE10, AA_8, STATE13, "", "", NULL},
431         {A_ASSOCIATE_RJ_PDU_RCV, STATE11, AA_8, STATE13, "", "", NULL},
432         {A_ASSOCIATE_RJ_PDU_RCV, STATE12, AA_8, STATE13, "", "", NULL},
433         {A_ASSOCIATE_RJ_PDU_RCV, STATE13, AA_6, STATE13, "", "", NULL}},
434 
435     {
436         {TRANS_CONN_INDICATION, STATE1, AE_5, STATE2, "", "", NULL},
437         {TRANS_CONN_INDICATION, STATE2, NOACTION, NOSTATE, "", "", NULL},
438         {TRANS_CONN_INDICATION, STATE3, NOACTION, NOSTATE, "", "", NULL},
439         {TRANS_CONN_INDICATION, STATE4, NOACTION, NOSTATE, "", "", NULL},
440         {TRANS_CONN_INDICATION, STATE5, NOACTION, NOSTATE, "", "", NULL},
441         {TRANS_CONN_INDICATION, STATE6, NOACTION, NOSTATE, "", "", NULL},
442         {TRANS_CONN_INDICATION, STATE7, NOACTION, NOSTATE, "", "", NULL},
443         {TRANS_CONN_INDICATION, STATE8, NOACTION, NOSTATE, "", "", NULL},
444         {TRANS_CONN_INDICATION, STATE9, NOACTION, NOSTATE, "", "", NULL},
445         {TRANS_CONN_INDICATION, STATE10, NOACTION, NOSTATE, "", "", NULL},
446         {TRANS_CONN_INDICATION, STATE11, NOACTION, NOSTATE, "", "", NULL},
447         {TRANS_CONN_INDICATION, STATE12, NOACTION, NOSTATE, "", "", NULL},
448         {TRANS_CONN_INDICATION, STATE13, NOACTION, NOSTATE, "", "", NULL}},
449 
450     {
451         {A_ASSOCIATE_RQ_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
452         {A_ASSOCIATE_RQ_PDU_RCV, STATE2, AE_6, NOSTATE, "", "", NULL},
453         {A_ASSOCIATE_RQ_PDU_RCV, STATE3, AA_8, STATE13, "", "", NULL},
454         {A_ASSOCIATE_RQ_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
455         {A_ASSOCIATE_RQ_PDU_RCV, STATE5, AA_8, STATE13, "", "", NULL},
456         {A_ASSOCIATE_RQ_PDU_RCV, STATE6, AA_8, STATE13, "", "", NULL},
457         {A_ASSOCIATE_RQ_PDU_RCV, STATE7, AA_8, STATE13, "", "", NULL},
458         {A_ASSOCIATE_RQ_PDU_RCV, STATE8, AA_8, STATE13, "", "", NULL},
459         {A_ASSOCIATE_RQ_PDU_RCV, STATE9, AA_8, STATE13, "", "", NULL},
460         {A_ASSOCIATE_RQ_PDU_RCV, STATE10, AA_8, STATE13, "", "", NULL},
461         {A_ASSOCIATE_RQ_PDU_RCV, STATE11, AA_8, STATE13, "", "", NULL},
462         {A_ASSOCIATE_RQ_PDU_RCV, STATE12, AA_8, STATE13, "", "", NULL},
463         {A_ASSOCIATE_RQ_PDU_RCV, STATE13, AA_7, STATE13, "", "", NULL}},
464 
465     {
466         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE1, NOACTION, NOSTATE, "", "", NULL},
467         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE2, NOACTION, NOSTATE, "", "", NULL},
468         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE3, AE_7, STATE6, "", "", NULL},
469         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE4, NOACTION, NOSTATE, "", "", NULL},
470         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE5, NOACTION, NOSTATE, "", "", NULL},
471         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE6, NOACTION, NOSTATE, "", "", NULL},
472         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE7, NOACTION, NOSTATE, "", "", NULL},
473         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE8, NOACTION, NOSTATE, "", "", NULL},
474         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE9, NOACTION, NOSTATE, "", "", NULL},
475         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE10, NOACTION, NOSTATE, "", "", NULL},
476         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE11, NOACTION, NOSTATE, "", "", NULL},
477         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE12, NOACTION, NOSTATE, "", "", NULL},
478         {A_ASSOCIATE_RESPONSE_ACCEPT, STATE13, NOACTION, NOSTATE, "", "", NULL}},
479 
480     {
481         {A_ASSOCIATE_RESPONSE_REJECT, STATE1, NOACTION, NOSTATE, "", "", NULL},
482         {A_ASSOCIATE_RESPONSE_REJECT, STATE2, NOACTION, NOSTATE, "", "", NULL},
483         {A_ASSOCIATE_RESPONSE_REJECT, STATE3, AE_8, STATE13, "", "", NULL},
484         {A_ASSOCIATE_RESPONSE_REJECT, STATE4, NOACTION, NOSTATE, "", "", NULL},
485         {A_ASSOCIATE_RESPONSE_REJECT, STATE5, NOACTION, NOSTATE, "", "", NULL},
486         {A_ASSOCIATE_RESPONSE_REJECT, STATE6, NOACTION, NOSTATE, "", "", NULL},
487         {A_ASSOCIATE_RESPONSE_REJECT, STATE7, NOACTION, NOSTATE, "", "", NULL},
488         {A_ASSOCIATE_RESPONSE_REJECT, STATE8, NOACTION, NOSTATE, "", "", NULL},
489         {A_ASSOCIATE_RESPONSE_REJECT, STATE9, NOACTION, NOSTATE, "", "", NULL},
490         {A_ASSOCIATE_RESPONSE_REJECT, STATE10, NOACTION, NOSTATE, "", "", NULL},
491         {A_ASSOCIATE_RESPONSE_REJECT, STATE11, NOACTION, NOSTATE, "", "", NULL},
492         {A_ASSOCIATE_RESPONSE_REJECT, STATE12, NOACTION, NOSTATE, "", "", NULL},
493         {A_ASSOCIATE_RESPONSE_REJECT, STATE13, NOACTION, NOSTATE, "", "", NULL}},
494 
495     {
496         {P_DATA_REQ, STATE1, NOACTION, NOSTATE, "", "", NULL},
497         {P_DATA_REQ, STATE2, NOACTION, NOSTATE, "", "", NULL},
498         {P_DATA_REQ, STATE3, NOACTION, NOSTATE, "", "", NULL},
499         {P_DATA_REQ, STATE4, NOACTION, NOSTATE, "", "", NULL},
500         {P_DATA_REQ, STATE5, NOACTION, NOSTATE, "", "", NULL},
501         {P_DATA_REQ, STATE6, DT_1, STATE6, "", "", NULL},
502         {P_DATA_REQ, STATE7, NOACTION, NOSTATE, "", "", NULL},
503         {P_DATA_REQ, STATE8, AR_7, STATE8, "", "", NULL},
504         {P_DATA_REQ, STATE9, NOACTION, NOSTATE, "", "", NULL},
505         {P_DATA_REQ, STATE10, NOACTION, NOSTATE, "", "", NULL},
506         {P_DATA_REQ, STATE11, NOACTION, NOSTATE, "", "", NULL},
507         {P_DATA_REQ, STATE12, NOACTION, NOSTATE, "", "", NULL},
508         {P_DATA_REQ, STATE13, NOACTION, NOSTATE, "", "", NULL}},
509 
510     {
511         {P_DATA_TF_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
512         {P_DATA_TF_PDU_RCV, STATE2, AA_1, STATE13, "", "", NULL},
513         {P_DATA_TF_PDU_RCV, STATE3, AA_8, STATE13, "", "", NULL},
514         {P_DATA_TF_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
515         {P_DATA_TF_PDU_RCV, STATE5, AA_8, STATE13, "", "", NULL},
516         {P_DATA_TF_PDU_RCV, STATE6, DT_2, STATE6, "", "", NULL},
517         {P_DATA_TF_PDU_RCV, STATE7, AR_6, STATE7, "", "", NULL},
518         {P_DATA_TF_PDU_RCV, STATE8, AA_8, STATE13, "", "", NULL},
519         {P_DATA_TF_PDU_RCV, STATE9, AA_8, STATE13, "", "", NULL},
520         {P_DATA_TF_PDU_RCV, STATE10, AA_8, STATE13, "", "", NULL},
521         {P_DATA_TF_PDU_RCV, STATE11, AA_8, STATE13, "", "", NULL},
522         {P_DATA_TF_PDU_RCV, STATE12, AA_8, STATE13, "", "", NULL},
523         {P_DATA_TF_PDU_RCV, STATE13, AA_6, STATE13, "", "", NULL}},
524 
525     {
526         {A_RELEASE_REQ, STATE1, NOACTION, NOSTATE, "", "", NULL},
527         {A_RELEASE_REQ, STATE2, NOACTION, NOSTATE, "", "", NULL},
528         {A_RELEASE_REQ, STATE3, NOACTION, NOSTATE, "", "", NULL},
529         {A_RELEASE_REQ, STATE4, NOACTION, NOSTATE, "", "", NULL},
530         {A_RELEASE_REQ, STATE5, NOACTION, NOSTATE, "", "", NULL},
531         {A_RELEASE_REQ, STATE6, AR_1, STATE7, "", "", NULL},
532         {A_RELEASE_REQ, STATE7, NOACTION, NOSTATE, "", "", NULL},
533         {A_RELEASE_REQ, STATE8, NOACTION, NOSTATE, "", "", NULL},
534         {A_RELEASE_REQ, STATE9, NOACTION, NOSTATE, "", "", NULL},
535         {A_RELEASE_REQ, STATE10, NOACTION, NOSTATE, "", "", NULL},
536         {A_RELEASE_REQ, STATE11, NOACTION, NOSTATE, "", "", NULL},
537         {A_RELEASE_REQ, STATE12, NOACTION, NOSTATE, "", "", NULL},
538         {A_RELEASE_REQ, STATE13, NOACTION, NOSTATE, "", "", NULL}},
539 
540     {
541         {A_RELEASE_RQ_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
542         {A_RELEASE_RQ_PDU_RCV, STATE2, AA_1, STATE13, "", "", NULL},
543         {A_RELEASE_RQ_PDU_RCV, STATE3, AA_8, STATE13, "", "", NULL},
544         {A_RELEASE_RQ_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
545         {A_RELEASE_RQ_PDU_RCV, STATE5, AA_8, STATE13, "", "", NULL},
546         {A_RELEASE_RQ_PDU_RCV, STATE6, AR_2, STATE8, "", "", NULL},
547         {A_RELEASE_RQ_PDU_RCV, STATE7, AR_8, NOSTATE, "", "", NULL},
548         {A_RELEASE_RQ_PDU_RCV, STATE8, AA_8, STATE13, "", "", NULL},
549         {A_RELEASE_RQ_PDU_RCV, STATE9, AA_8, STATE13, "", "", NULL},
550         {A_RELEASE_RQ_PDU_RCV, STATE10, AA_8, STATE13, "", "", NULL},
551         {A_RELEASE_RQ_PDU_RCV, STATE11, AA_8, STATE13, "", "", NULL},
552         {A_RELEASE_RQ_PDU_RCV, STATE12, AA_8, STATE13, "", "", NULL},
553         {A_RELEASE_RQ_PDU_RCV, STATE13, AA_6, STATE13, "", "", NULL}},
554 
555     {
556         {A_RELEASE_RP_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
557         {A_RELEASE_RP_PDU_RCV, STATE2, AA_1, STATE13, "", "", NULL},
558         {A_RELEASE_RP_PDU_RCV, STATE3, AA_8, STATE13, "", "", NULL},
559         {A_RELEASE_RP_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
560         {A_RELEASE_RP_PDU_RCV, STATE5, AA_8, STATE13, "", "", NULL},
561         {A_RELEASE_RP_PDU_RCV, STATE6, AA_8, STATE13, "", "", NULL},
562         {A_RELEASE_RP_PDU_RCV, STATE7, AR_3, STATE1, "", "", NULL},
563         {A_RELEASE_RP_PDU_RCV, STATE8, AA_8, STATE13, "", "", NULL},
564         {A_RELEASE_RP_PDU_RCV, STATE9, AA_8, STATE13, "", "", NULL},
565         {A_RELEASE_RP_PDU_RCV, STATE10, AR_10, STATE12, "", "", NULL},
566         {A_RELEASE_RP_PDU_RCV, STATE11, AR_3, STATE1, "", "", NULL},
567         {A_RELEASE_RP_PDU_RCV, STATE12, AA_8, STATE13, "", "", NULL},
568         {A_RELEASE_RP_PDU_RCV, STATE13, AA_6, STATE13, "", "", NULL}},
569 
570     {
571         {A_RELEASE_RESP, STATE1, NOACTION, NOSTATE, "", "", NULL},
572         {A_RELEASE_RESP, STATE2, NOACTION, NOSTATE, "", "", NULL},
573         {A_RELEASE_RESP, STATE3, NOACTION, NOSTATE, "", "", NULL},
574         {A_RELEASE_RESP, STATE4, NOACTION, NOSTATE, "", "", NULL},
575         {A_RELEASE_RESP, STATE5, NOACTION, NOSTATE, "", "", NULL},
576         {A_RELEASE_RESP, STATE6, NOACTION, NOSTATE, "", "", NULL},
577         {A_RELEASE_RESP, STATE7, NOACTION, NOSTATE, "", "", NULL},
578         {A_RELEASE_RESP, STATE8, AR_4, STATE13, "", "", NULL},
579         {A_RELEASE_RESP, STATE9, AR_9, STATE11, "", "", NULL},
580         {A_RELEASE_RESP, STATE10, NOACTION, NOSTATE, "", "", NULL},
581         {A_RELEASE_RESP, STATE11, NOACTION, NOSTATE, "", "", NULL},
582         {A_RELEASE_RESP, STATE12, AR_4, STATE13, "", "", NULL},
583         {A_RELEASE_RESP, STATE13, NOACTION, NOSTATE, "", "", NULL}},
584 
585     {
586         {A_ABORT_REQ, STATE1, NOACTION, NOSTATE, "", "", NULL},
587         {A_ABORT_REQ, STATE2, NOACTION, NOSTATE, "", "", NULL},
588         {A_ABORT_REQ, STATE3, AA_1, STATE13, "", "", NULL},
589         {A_ABORT_REQ, STATE4, AA_2, STATE1, "", "", NULL},
590         {A_ABORT_REQ, STATE5, AA_1, STATE13, "", "", NULL},
591         {A_ABORT_REQ, STATE6, AA_1, STATE13, "", "", NULL},
592         {A_ABORT_REQ, STATE7, AA_1, STATE13, "", "", NULL},
593         {A_ABORT_REQ, STATE8, AA_1, STATE13, "", "", NULL},
594         {A_ABORT_REQ, STATE9, AA_1, STATE13, "", "", NULL},
595         {A_ABORT_REQ, STATE10, AA_1, STATE13, "", "", NULL},
596         {A_ABORT_REQ, STATE11, AA_1, STATE13, "", "", NULL},
597         {A_ABORT_REQ, STATE12, AA_1, STATE13, "", "", NULL},
598         {A_ABORT_REQ, STATE13, NOACTION, NOSTATE, "", "", NULL}},
599 
600     {
601         {A_ABORT_PDU_RCV, STATE1, NOACTION, NOSTATE, "", "", NULL},
602         {A_ABORT_PDU_RCV, STATE2, AA_2, STATE1, "", "", NULL},
603         {A_ABORT_PDU_RCV, STATE3, AA_3, STATE1, "", "", NULL},
604         {A_ABORT_PDU_RCV, STATE4, NOACTION, NOSTATE, "", "", NULL},
605         {A_ABORT_PDU_RCV, STATE5, AA_3, STATE1, "", "", NULL},
606         {A_ABORT_PDU_RCV, STATE6, AA_3, STATE1, "", "", NULL},
607         {A_ABORT_PDU_RCV, STATE7, AA_3, STATE1, "", "", NULL},
608         {A_ABORT_PDU_RCV, STATE8, AA_3, STATE1, "", "", NULL},
609         {A_ABORT_PDU_RCV, STATE9, AA_3, STATE1, "", "", NULL},
610         {A_ABORT_PDU_RCV, STATE10, AA_3, STATE1, "", "", NULL},
611         {A_ABORT_PDU_RCV, STATE11, AA_3, STATE1, "", "", NULL},
612         {A_ABORT_PDU_RCV, STATE12, AA_3, STATE1, "", "", NULL},
613         {A_ABORT_PDU_RCV, STATE13, AA_2, STATE1, "", "", NULL}},
614 
615     {
616         {TRANS_CONN_CLOSED, STATE1, NOACTION, NOSTATE, "", "", NULL},
617         {TRANS_CONN_CLOSED, STATE2, AA_5, STATE1, "", "", NULL},
618         {TRANS_CONN_CLOSED, STATE3, AA_4, STATE1, "", "", NULL},
619         {TRANS_CONN_CLOSED, STATE4, AA_4, STATE1, "", "", NULL},
620         {TRANS_CONN_CLOSED, STATE5, AA_4, STATE1, "", "", NULL},
621         {TRANS_CONN_CLOSED, STATE6, AA_4, STATE1, "", "", NULL},
622         {TRANS_CONN_CLOSED, STATE7, AA_4, STATE1, "", "", NULL},
623         {TRANS_CONN_CLOSED, STATE8, AA_4, STATE1, "", "", NULL},
624         {TRANS_CONN_CLOSED, STATE9, AA_4, STATE1, "", "", NULL},
625         {TRANS_CONN_CLOSED, STATE10, AA_4, STATE1, "", "", NULL},
626         {TRANS_CONN_CLOSED, STATE11, AA_4, STATE1, "", "", NULL},
627         {TRANS_CONN_CLOSED, STATE12, AA_4, STATE1, "", "", NULL},
628         {TRANS_CONN_CLOSED, STATE13, AR_5, STATE1, "", "", NULL}},
629 
630     {
631         {ARTIM_TIMER_EXPIRED, STATE1, NOACTION, NOSTATE, "", "", NULL},
632         {ARTIM_TIMER_EXPIRED, STATE2, AA_2T, STATE1, "", "", NULL},
633         {ARTIM_TIMER_EXPIRED, STATE3, NOACTION, NOSTATE, "", "", NULL},
634         {ARTIM_TIMER_EXPIRED, STATE4, NOACTION, NOSTATE, "", "", NULL},
635 
636         // DICOM part 8 does not define an action and state for the
637         // situation where a timeout occurs while we are waiting for an
638         // incoming A-ASSOCIATE-AC or A-ASSOCIATE-RJ. We close the transport
639         // connection, return an error code indicating a timeout,
640         // and reset the FSM to idle state (STATE1).
641         {ARTIM_TIMER_EXPIRED, STATE5, AA_2T, STATE1, "", "", NULL},
642 
643         {ARTIM_TIMER_EXPIRED, STATE6, NOACTION, NOSTATE, "", "", NULL},
644 
645         // DICOM part 8 does not define an action and state for the
646         // situation where a timeout occurs while we are waiting for an
647         // incoming A-RELEASE-RSP. We close the transport
648         // connection, return an error code indicating a timeout,
649         // and reset the FSM to idle state (STATE1).
650         {ARTIM_TIMER_EXPIRED, STATE7, AA_2T, STATE1, "", "", NULL},
651 
652         {ARTIM_TIMER_EXPIRED, STATE8, NOACTION, NOSTATE, "", "", NULL},
653         {ARTIM_TIMER_EXPIRED, STATE9, NOACTION, NOSTATE, "", "", NULL},
654         {ARTIM_TIMER_EXPIRED, STATE10, NOACTION, NOSTATE, "", "", NULL},
655         {ARTIM_TIMER_EXPIRED, STATE11, NOACTION, NOSTATE, "", "", NULL},
656         {ARTIM_TIMER_EXPIRED, STATE12, NOACTION, NOSTATE, "", "", NULL},
657         {ARTIM_TIMER_EXPIRED, STATE13, AA_2, STATE1, "", "", NULL}},
658 
659     {
660         {INVALID_PDU, STATE1, NOACTION, NOSTATE, "", "", NULL},
661         {INVALID_PDU, STATE2, AA_1, STATE13, "", "", NULL},
662         {INVALID_PDU, STATE3, AA_8, STATE13, "", "", NULL},
663         {INVALID_PDU, STATE4, NOACTION, NOSTATE, "", "", NULL},
664         {INVALID_PDU, STATE5, AA_8, STATE13, "", "", NULL},
665         {INVALID_PDU, STATE6, AA_8, STATE13, "", "", NULL},
666         {INVALID_PDU, STATE7, AA_8, STATE13, "", "", NULL},
667         {INVALID_PDU, STATE8, AA_8, STATE13, "", "", NULL},
668         {INVALID_PDU, STATE9, AA_8, STATE13, "", "", NULL},
669         {INVALID_PDU, STATE10, AA_8, STATE13, "", "", NULL},
670         {INVALID_PDU, STATE11, AA_8, STATE13, "", "", NULL},
671         {INVALID_PDU, STATE12, AA_8, STATE13, "", "", NULL},
672         {INVALID_PDU, STATE13, AA_7, STATE13, "", "", NULL}}
673 };
674 
675 
676 /* Dul_InitializeFSM
677 **
678 ** Purpose:
679 **      Initialize the DUL finite state machine by filling in addresses of
680 **      functions.
681 **
682 ** Parameter Dictionary:
683 **      None
684 **
685 ** Return Values:
686 **
687 ** Notes:
688 **
689 ** Algorithm:
690 **      Description of the algorithm (optional) and any other notes.
691 */
692 OFCondition
DUL_InitializeFSM()693 DUL_InitializeFSM()
694 {
695     unsigned long
696         l_index,
697         idx2;
698     FSM_ENTRY
699         * stateEntries;
700 
701     stateEntries = (FSM_ENTRY *) StateTable;
702     for (l_index = 0; l_index < DUL_NUMBER_OF_EVENTS * DUL_NUMBER_OF_STATES; l_index++) {
703         if (stateEntries[l_index].action != NOACTION) {
704             for (idx2 = 0; idx2 < DIM_OF(FSM_FunctionTable) &&
705                  stateEntries[l_index].actionFunction == NULL; idx2++)
706                 if (stateEntries[l_index].action == FSM_FunctionTable[idx2].action) {
707                     stateEntries[l_index].actionFunction =
708                         FSM_FunctionTable[idx2].actionFunction;
709                     (void) sprintf(stateEntries[l_index].actionName, "%.*s",
710                                  (int)(sizeof(stateEntries[l_index].actionName) - 1),
711                                    FSM_FunctionTable[idx2].actionName);
712                 }
713         }
714         for (idx2 = 0; idx2 < DIM_OF(Event_Table) &&
715              strlen(stateEntries[l_index].eventName) == 0; idx2++) {
716             if (stateEntries[l_index].event == Event_Table[idx2].event)
717                 (void) sprintf(stateEntries[l_index].eventName, "%.*s",
718                                (int)(sizeof(stateEntries[l_index].eventName) - 1),
719                                Event_Table[idx2].eventName);
720         }
721     }
722 
723     return EC_Normal;
724 }
725 
726 
727 
728 /* PRV_StateMachine
729 **
730 ** Purpose:
731 **      Execute the action function, given the current state and the event.
732 **
733 ** Parameter Dictionary:
734 **      network         Handle to the network environment
735 **      association     Handle to the Association
736 **      event           The event that will trigger this action
737 **      state           Current state of the finite state machine.
738 **      params          Service parameters describing this Association
739 **
740 ** Return Values:
741 **
742 **
743 ** Notes:
744 **
745 ** Algorithm:
746 **      Description of the algorithm (optional) and any other notes.
747 */
748 OFCondition
PRV_StateMachine(PRIVATE_NETWORKKEY ** network,PRIVATE_ASSOCIATIONKEY ** association,int event,int state,void * params)749 PRV_StateMachine(PRIVATE_NETWORKKEY ** network,
750                  PRIVATE_ASSOCIATIONKEY ** association, int event, int state,
751                  void *params)
752 {
753     volatile FSM_ENTRY
754         * entry;
755 
756     /* check if the given event is valid, if not return an error */
757     if (event < 0 || event >= DUL_NUMBER_OF_EVENTS)
758     {
759       char buf1[256];
760       sprintf(buf1, "DUL Finite State Machine Error: Bad event, state %d event %d", state, event);
761       return makeDcmnetCondition(DULC_FSMERROR, OF_error, buf1);
762     }
763 
764     /* check if the given state is valid, if not return an error */
765     if (state < 1 || state > DUL_NUMBER_OF_STATES)
766     {
767       char buf1[256];
768       sprintf(buf1, "DUL Finite State Machine Error: Bad state, state %d event %d", state, event);
769       return makeDcmnetCondition(DULC_FSMERROR, OF_error, buf1);
770     }
771 
772     /* depending on the given event and state, determine the state table's entry (the state */
773     /* table implements the state transition table of DICOM's Upper Layer State Machine which */
774     /* in turn implements the DICOM upper layer protocol) (see DICOM standard (year 2000) part */
775     /* 8, section 9) (or the corresponding section in a later version of the standard) */
776     entry = &StateTable[event][state - 1];
777 
778     /* dump information if required */
779     DCMNET_TRACE("DUL  FSM Table: State: " << state << " Event: " << event << OFendl
780             << "DUL  Event:  " << OFconst_cast(const char *, entry->eventName) << OFendl
781             << "DUL  Action: " << OFconst_cast(const char *, entry->actionName));
782 
783     /* if the state table's entry specifies an action function, execute this function and return */
784     /* it's result value. If there is no action function defined, return a corresponding error. */
785     if (entry->actionFunction != NULL)
786         return entry->actionFunction(network, association, entry->nextState, params);
787     else
788     {
789       char buf1[256];
790       sprintf(buf1, "DUL Finite State Machine Error: No action defined, state %d event %d", state, event);
791       return makeDcmnetCondition(DULC_FSMERROR, OF_error, buf1);
792     }
793 }
794 
795 /* ============================================================
796 **
797 **  Private functions (local to this module) defined below.
798 */
799 
800 /* AE_1_TransportConnect
801 **
802 ** Purpose:
803 **      Issue a TRANSPORT_CONNECT request primitive to local transport
804 **      service.
805 **
806 ** Parameter Dictionary:
807 **      network         Handle to the network environment
808 **      association     Handle to the Association
809 **      nextState       The next state to be reached from the current state
810 **      params          Service parameters describing the Association
811 **
812 ** Return Values:
813 **
814 ** Notes:
815 **
816 ** Algorithm:
817 **      Description of the algorithm (optional) and any other notes.
818 */
819 static OFCondition
AE_1_TransportConnect(PRIVATE_NETWORKKEY ** network,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)820 AE_1_TransportConnect(PRIVATE_NETWORKKEY ** network,
821          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
822 {
823     DUL_ASSOCIATESERVICEPARAMETERS
824     * service;
825     OFCondition cond = EC_Normal;
826 
827     service = (DUL_ASSOCIATESERVICEPARAMETERS *) params;
828     clearPDUCache(association);
829     cond = requestAssociationTCP(network, service, association);
830     (*association)->protocolState = nextState;
831     return cond;
832 }
833 
834 /* AE_2_SendAssociateRQPDU
835 **
836 ** Purpose:
837 **      Send A-ASSOCIATE-RQ PDU.
838 **
839 ** Parameter Dictionary:
840 **
841 **      network         Handle to the network environment
842 **      association     Handle to the Association
843 **      nextState       The next state to be reached from the current state
844 **      params          Service parameters describing the Association
845 **
846 ** Return Values:
847 **
848 **
849 ** Notes:
850 **
851 ** Algorithm:
852 **      Description of the algorithm (optional) and any other notes.
853 */
854 static OFCondition
AE_2_SendAssociateRQPDU(PRIVATE_NETWORKKEY ** network,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)855 AE_2_SendAssociateRQPDU(PRIVATE_NETWORKKEY ** network,
856          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
857 {
858     DUL_ASSOCIATESERVICEPARAMETERS
859     * service;
860     OFCondition cond = EC_Normal;
861 
862     service = (DUL_ASSOCIATESERVICEPARAMETERS *) params;
863 
864     cond = sendAssociationRQTCP(network, service, association);
865     (*association)->protocolState = nextState;
866     return cond;
867 }
868 
869 
870 /* AE_3_AssociateConfirmationAccept
871 **
872 ** Purpose:
873 **      Issue an A-ASSOCIATE confirmation (Accept) primitive
874 **
875 ** Parameter Dictionary:
876 **
877 **      network         Handle to the network environment
878 **      association     Handle to the Association
879 **      nextState       The next state to be reached from the current state
880 **      params          Service parameters describing the Association
881 **
882 ** Return Values:
883 **
884 **
885 ** Notes:
886 **
887 ** Algorithm:
888 **      Description of the algorithm (optional) and any other notes.
889 */
890 
891 static OFCondition
AE_3_AssociateConfirmationAccept(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)892 AE_3_AssociateConfirmationAccept(PRIVATE_NETWORKKEY ** /*network*/,
893          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
894 {
895     DUL_ASSOCIATESERVICEPARAMETERS
896     * service;
897     unsigned char
898         * buffer = NULL,
899         pduType,
900         pduReserve;
901     unsigned long
902         pduLength;
903     PRV_ASSOCIATEPDU
904         assoc;
905     PRV_PRESENTATIONCONTEXTITEM
906         * prvCtx;
907     DUL_PRESENTATIONCONTEXT
908         * userPresentationCtx,
909         * requestedPresentationCtx;
910     DUL_SUBITEM
911         * subItem;
912     PRV_SCUSCPROLE
913         * scuscpRole;
914 
915     service = (DUL_ASSOCIATESERVICEPARAMETERS *) params;
916     OFCondition cond = readPDU(association, DUL_BLOCK, 0, &buffer, &pduType, &pduReserve, &pduLength);
917 
918     if (cond.bad())
919     {
920        if (buffer) free(buffer);
921        return cond;
922     }
923 
924     /* cond is good so we know that buffer exists */
925 
926     DCMNET_DEBUG(dump_pdu("Associate Accept", buffer, pduLength + 6));
927 
928     if (pduType == DUL_TYPEASSOCIATEAC)
929     {
930         if ((*association)->associatePDUFlag)
931         {
932           // copy A-ASSOCIATE-AC PDU
933           (*association)->associatePDU = new char[pduLength+6];
934           if ((*association)->associatePDU)
935           {
936             memcpy((*association)->associatePDU, buffer, (size_t) pduLength+6);
937             (*association)->associatePDULength = pduLength+6;
938           }
939         }
940 
941         cond = parseAssociate(buffer, pduLength, &assoc);
942         free(buffer);
943         if (cond.bad()) return makeDcmnetSubCondition(DULC_ILLEGALPDU, OF_error, "DUL Illegal or ill-formed PDU", cond);
944 
945         OFStandard::strlcpy(service->respondingAPTitle, assoc.calledAPTitle, sizeof(service->respondingAPTitle));
946         OFStandard::strlcpy(service->callingAPTitle, assoc.callingAPTitle, sizeof(service->callingAPTitle));
947         OFStandard::strlcpy(service->applicationContextName, assoc.applicationContext.data, sizeof(service->applicationContextName));
948 
949         if ((service->acceptedPresentationContext = LST_Create()) == NULL) return EC_MemoryExhausted;
950 
951         prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Head(&assoc.presentationContextList);
952         if (prvCtx != NULL)
953             (void) LST_Position(&assoc.presentationContextList, (LST_NODE*)prvCtx);
954         while (prvCtx != NULL) {
955             userPresentationCtx = (DUL_PRESENTATIONCONTEXT*)malloc(sizeof(DUL_PRESENTATIONCONTEXT));
956             if (userPresentationCtx == NULL) return EC_MemoryExhausted;
957 
958             (void) memset(userPresentationCtx, 0, sizeof(DUL_PRESENTATIONCONTEXT));
959             userPresentationCtx->result = prvCtx->result;
960             userPresentationCtx->presentationContextID = prvCtx->contextID;
961             userPresentationCtx->proposedTransferSyntax = NULL;
962             requestedPresentationCtx = findPresentationCtx(
963                  &service->requestedPresentationContext, prvCtx->contextID);
964             if (requestedPresentationCtx != NULL) {
965                 OFStandard::strlcpy(userPresentationCtx->abstractSyntax,
966                     requestedPresentationCtx->abstractSyntax,
967                     sizeof(userPresentationCtx->abstractSyntax));
968                 userPresentationCtx->proposedSCRole =
969                     requestedPresentationCtx->proposedSCRole;
970             }
971             userPresentationCtx->acceptedSCRole = DUL_SC_ROLE_DEFAULT;
972             scuscpRole = findSCUSCPRole(&assoc.userInfo.SCUSCPRoleList,
973                                         userPresentationCtx->abstractSyntax);
974             if (scuscpRole != NULL) {
975                 if ((scuscpRole->SCURole == 0) && (scuscpRole->SCPRole == 0))
976                     userPresentationCtx->acceptedSCRole = DUL_SC_ROLE_NONE;
977                 else if ((scuscpRole->SCURole == 1) && (scuscpRole->SCPRole == 1))
978                     userPresentationCtx->acceptedSCRole = DUL_SC_ROLE_SCUSCP;
979                 else if (scuscpRole->SCURole == 1)
980                     userPresentationCtx->acceptedSCRole = DUL_SC_ROLE_SCU;
981                 else  // SCPRole == 1
982                     userPresentationCtx->acceptedSCRole = DUL_SC_ROLE_SCP;
983             }
984             if (prvCtx->transferSyntaxList == NULL)
985             {
986               char buf1[256];
987               sprintf(buf1, "DUL Peer supplied illegal number of transfer syntaxes (%d)", 0);
988               free(userPresentationCtx);
989               return makeDcmnetCondition(DULC_PEERILLEGALXFERSYNTAXCOUNT, OF_error, buf1);
990             }
991 
992             if ((prvCtx->result == DUL_PRESENTATION_ACCEPT) && (LST_Count(&prvCtx->transferSyntaxList) != 1))
993             {
994               char buf2[256];
995               sprintf(buf2, "DUL Peer supplied illegal number of transfer syntaxes (%ld)", LST_Count(&prvCtx->transferSyntaxList));
996               free(userPresentationCtx);
997               return makeDcmnetCondition(DULC_PEERILLEGALXFERSYNTAXCOUNT, OF_error, buf2);
998             }
999             subItem = (DUL_SUBITEM*)LST_Head(&prvCtx->transferSyntaxList);
1000             if (subItem != NULL)
1001                 OFStandard::strlcpy(userPresentationCtx->acceptedTransferSyntax,
1002                               subItem->data, sizeof(userPresentationCtx->acceptedTransferSyntax));
1003             LST_Enqueue(&service->acceptedPresentationContext, (LST_NODE*)userPresentationCtx);
1004 
1005             prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Next(&assoc.presentationContextList);
1006 
1007         }
1008 
1009         /* extended negotiation */
1010         if (assoc.userInfo.extNegList != NULL) {
1011             service->acceptedExtNegList = new SOPClassExtendedNegotiationSubItemList;
1012             if (service->acceptedExtNegList == NULL)  return EC_MemoryExhausted;
1013             appendList(*assoc.userInfo.extNegList, *service->acceptedExtNegList);
1014         }
1015 
1016         /* user identity negotiation */
1017         if (assoc.userInfo.usrIdent != NULL) {
1018           service->ackUserIdentNeg =
1019             new UserIdentityNegotiationSubItemAC( *(OFstatic_cast(UserIdentityNegotiationSubItemAC*, assoc.userInfo.usrIdent)));
1020           if (service->ackUserIdentNeg == NULL)  return EC_MemoryExhausted;
1021 
1022         }
1023 
1024         destroyPresentationContextList(&assoc.presentationContextList);
1025         destroyUserInformationLists(&assoc.userInfo);
1026         service->peerMaxPDU = assoc.userInfo.maxLength.maxLength;
1027         (*association)->maxPDV = assoc.userInfo.maxLength.maxLength;
1028         (*association)->maxPDVAcceptor =
1029             assoc.userInfo.maxLength.maxLength;
1030         OFStandard::strlcpy(service->calledImplementationClassUID,
1031                assoc.userInfo.implementationClassUID.data, DICOM_UI_LENGTH + 1);
1032         OFStandard::strlcpy(service->calledImplementationVersionName,
1033                assoc.userInfo.implementationVersionName.data, 16 + 1);
1034 
1035         (*association)->associationState = DUL_ASSOC_ESTABLISHED;
1036         (*association)->protocolState = nextState;
1037         return EC_Normal;
1038     }
1039     return DUL_UNEXPECTEDPDU;
1040 }
1041 
1042 /* AE_4_AssociateConfirmationReject
1043 **
1044 ** Purpose:
1045 **      Issue A-ASSOCIATE confirmation reject primitive and close transport
1046 **      connection.
1047 **
1048 ** Parameter Dictionary:
1049 **
1050 **      network         Handle to the network environment
1051 **      association     Handle to the Association
1052 **      nextState       The next state to be reached from the current state
1053 **      params          Service parameters describing the Association
1054 **
1055 ** Return Values:
1056 **
1057 **
1058 ** Notes:
1059 **
1060 ** Algorithm:
1061 **      Description of the algorithm (optional) and any other notes.
1062 */
1063 
1064 static OFCondition
AE_4_AssociateConfirmationReject(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)1065 AE_4_AssociateConfirmationReject(PRIVATE_NETWORKKEY ** /*network*/,
1066          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
1067 {
1068     DUL_ASSOCIATESERVICEPARAMETERS
1069     * service;
1070     unsigned char
1071         buffer[128],
1072         pduType,
1073         pduReserve;
1074     unsigned long
1075         pduLength;
1076 
1077     service = (DUL_ASSOCIATESERVICEPARAMETERS *) params;
1078     OFCondition cond = readPDUBody(association, DUL_BLOCK, 0, buffer, sizeof(buffer),
1079                        &pduType, &pduReserve, &pduLength);
1080     if (cond.bad())
1081         return cond;
1082 
1083     if (pduType == DUL_TYPEASSOCIATERJ) {
1084         service->result = buffer[1];
1085         service->resultSource = buffer[2];
1086         service->diagnostic = buffer[3];
1087         (*association)->protocolState = nextState;
1088         closeTransport(association);
1089         cond = DUL_ASSOCIATIONREJECTED;
1090     } else cond = DUL_UNEXPECTEDPDU;
1091 
1092     return cond;
1093 }
1094 
1095 
1096 /* AE_5_TransportConnectResponse
1097 **
1098 ** Purpose:
1099 **      Issue Transport connection response primitive and start ARTIM timer.
1100 **
1101 ** Parameter Dictionary:
1102 **
1103 **      network         Handle to the network environment
1104 **      association     Handle to the Association
1105 **      nextState       The next state to be reached from the current state
1106 **      params          Service parameters describing the Association
1107 **
1108 ** Return Values:
1109 
1110 **
1111 ** Notes:
1112 **
1113 ** Algorithm:
1114 **      Description of the algorithm (optional) and any other notes.
1115 */
1116 
1117 static OFCondition
AE_5_TransportConnectResponse(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1118 AE_5_TransportConnectResponse(PRIVATE_NETWORKKEY ** /*network*/,
1119          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1120 {
1121     clearPDUCache(association);
1122     (*association)->protocolState = nextState;
1123 
1124     /* Start the timer (?) */
1125 
1126     return EC_Normal;
1127 }
1128 
1129 
1130 
1131 /* AE_6_ExamineAssociateRequest
1132 **
1133 ** Purpose:
1134 **      Stop ARTIM timer and if A-ASSOCIATE-RQ acceptable by service-provider,
1135 **      issue A-ASSOCIATE indication primitive else issue A-ASSOCIATE
1136 **      indication primitive.
1137 **
1138 ** Parameter Dictionary:
1139 **
1140 **      network         Handle to the network environment
1141 **      association     Handle to the Association
1142 **      nextState       The next state to be reached from the current state
1143 **      params          Service parameters describing the Association
1144 **
1145 ** Return Values:
1146 **
1147 **
1148 ** Notes:
1149 **
1150 ** Algorithm:
1151 **      Description of the algorithm (optional) and any other notes.
1152 */
1153 
1154 static OFCondition
AE_6_ExamineAssociateRequest(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int,void * params)1155 AE_6_ExamineAssociateRequest(PRIVATE_NETWORKKEY ** /*network*/,
1156          PRIVATE_ASSOCIATIONKEY ** association, int /*nextState*/, void *params)
1157 {
1158     DUL_ASSOCIATESERVICEPARAMETERS
1159     * service;
1160     unsigned char
1161         *buffer=NULL,
1162         pduType,
1163         pduReserve;
1164     unsigned long
1165         pduLength;
1166     PRV_ASSOCIATEPDU
1167         assoc;
1168 
1169     (*association)->timerStart = 0;
1170     service = (DUL_ASSOCIATESERVICEPARAMETERS *) params;
1171     OFCondition cond = readPDU(association, DUL_BLOCK, 0, &buffer,
1172                    &pduType, &pduReserve, &pduLength);
1173 
1174     if (cond.bad())
1175     {
1176        if (buffer) free(buffer);
1177        return cond;
1178     }
1179 
1180     /* cond is good so we know that buffer exists */
1181 
1182     if (pduType == DUL_TYPEASSOCIATERQ)
1183     {
1184         if ((*association)->associatePDUFlag)
1185         {
1186           // copy A-ASSOCIATE-RQ PDU
1187           (*association)->associatePDU = new char[pduLength+6];
1188           if ((*association)->associatePDU)
1189           {
1190             memcpy((*association)->associatePDU, buffer, (size_t) pduLength+6);
1191             (*association)->associatePDULength = pduLength+6;
1192           }
1193         }
1194 
1195         DCMNET_DEBUG(dump_pdu("Associate Request", buffer, pduLength + 6));
1196         cond = parseAssociate(buffer, pduLength, &assoc);
1197         free(buffer);
1198         buffer = NULL;
1199 
1200         if (cond.bad()) {
1201             if (cond == DUL_UNSUPPORTEDPEERPROTOCOL)    /* Make it look OK */
1202                 (*association)->protocolState = STATE3;
1203             return cond;
1204         }
1205         OFStandard::strlcpy(service->calledAPTitle, assoc.calledAPTitle, sizeof(service->calledAPTitle));
1206         OFStandard::strlcpy(service->callingAPTitle, assoc.callingAPTitle, sizeof(service->callingAPTitle));
1207         OFStandard::strlcpy(service->applicationContextName, assoc.applicationContext.data, sizeof(service->applicationContextName));
1208 
1209         if ((service->requestedPresentationContext = LST_Create()) == NULL) return EC_MemoryExhausted;
1210         if (translatePresentationContextList(&assoc.presentationContextList,
1211                                              &assoc.userInfo.SCUSCPRoleList,
1212                                              &service->requestedPresentationContext).bad())
1213         {
1214             return DUL_PCTRANSLATIONFAILURE;
1215         }
1216 
1217         /* extended negotiation */
1218         if (assoc.userInfo.extNegList != NULL) {
1219             service->requestedExtNegList = new SOPClassExtendedNegotiationSubItemList;
1220             if (service->requestedExtNegList == NULL) return EC_MemoryExhausted;
1221             appendList(*assoc.userInfo.extNegList, *service->requestedExtNegList);
1222         }
1223 
1224         /* user identity negotiation: Remember request values in association parameters (copy)*/
1225         if (assoc.userInfo.usrIdent != NULL) {
1226           service->reqUserIdentNeg = new UserIdentityNegotiationSubItemRQ();
1227           if (service->reqUserIdentNeg == NULL) return EC_MemoryExhausted;
1228             *(service->reqUserIdentNeg) = *(OFstatic_cast(UserIdentityNegotiationSubItemRQ*,assoc.userInfo.usrIdent));
1229         }
1230 
1231         service->peerMaxPDU = assoc.userInfo.maxLength.maxLength;
1232         (*association)->maxPDV = assoc.userInfo.maxLength.maxLength;
1233         (*association)->maxPDVRequestor =
1234             assoc.userInfo.maxLength.maxLength;
1235         OFStandard::strlcpy(service->callingImplementationClassUID,
1236                assoc.userInfo.implementationClassUID.data, DICOM_UI_LENGTH + 1);
1237         OFStandard::strlcpy(service->callingImplementationVersionName,
1238                assoc.userInfo.implementationVersionName.data, 16 + 1);
1239         (*association)->associationState = DUL_ASSOC_ESTABLISHED;
1240 
1241         destroyPresentationContextList(&assoc.presentationContextList);
1242         destroyUserInformationLists(&assoc.userInfo);
1243 
1244         /* If this PDU is ok with us */
1245         (*association)->protocolState = STATE3;
1246 
1247         return EC_Normal;
1248     }
1249     return DUL_UNEXPECTEDPDU;
1250 }
1251 
1252 
1253 /* AE_7_SendAssociateAC
1254 **
1255 ** Purpose:
1256 **      Send A-ASSOCIATE-AC PDU.
1257 **
1258 ** Parameter Dictionary:
1259 **
1260 **      network         Handle to the network environment
1261 **      association     Handle to the Association
1262 **      nextState       The next state to be reached from the current state
1263 **      params          Service parameters describing the Association
1264 **
1265 ** Return Values:
1266 **
1267 **
1268 ** Algorithm:
1269 **      Description of the algorithm (optional) and any other notes.
1270 */
1271 
1272 static OFCondition
AE_7_SendAssociateAC(PRIVATE_NETWORKKEY ** network,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)1273 AE_7_SendAssociateAC(PRIVATE_NETWORKKEY ** network,
1274          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
1275 {
1276     DUL_ASSOCIATESERVICEPARAMETERS
1277     * service;
1278     OFCondition cond = EC_Normal;
1279 
1280     service = (DUL_ASSOCIATESERVICEPARAMETERS *) params;
1281     cond = sendAssociationACTCP(network, service, association);
1282     (*association)->protocolState = nextState;
1283     return cond;
1284 }
1285 
1286 
1287 /* AE_8_SendAssociateRJ
1288 **
1289 ** Purpose:
1290 **      Send A-ASSOCIATE-RJ PDU and start ARTIM timer.
1291 **
1292 ** Parameter Dictionary:
1293 **
1294 **      network         Handle to the network environment
1295 **      association     Handle to the Association
1296 **      nextState       The next state to be reached from the current state
1297 **      params          Service parameters describing the Association
1298 **
1299 ** Return Values:
1300 **
1301 **
1302 ** Notes:
1303 **
1304 ** Algorithm:
1305 **      Description of the algorithm (optional) and any other notes.
1306 */
1307 
1308 static OFCondition
AE_8_SendAssociateRJ(PRIVATE_NETWORKKEY ** network,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)1309 AE_8_SendAssociateRJ(PRIVATE_NETWORKKEY ** network,
1310          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
1311 {
1312     DUL_ABORTITEMS
1313     * abortItems;
1314     OFCondition cond = EC_Normal;
1315 
1316     abortItems = (DUL_ABORTITEMS *) params;
1317     cond = sendAssociationRJTCP(network, abortItems, association);
1318     (*association)->protocolState = nextState;
1319 
1320     /* Start the timer (?) */
1321 
1322     return cond;
1323 }
1324 
1325 /* DT_1_SendPData
1326 **
1327 ** Purpose:
1328 **      Send P-DATA-TF PDU
1329 **
1330 ** Parameter Dictionary:
1331 **
1332 **      network         Handle to the network environment
1333 **      association     Handle to the Association
1334 **      nextState       The next state to be reached from the current state
1335 **      params          Service parameters describing the Association
1336 **
1337 ** Return Values:
1338 **
1339 **
1340 ** Notes:
1341 **
1342 ** Algorithm:
1343 **      Description of the algorithm (optional) and any other notes.
1344 */
1345 
1346 static OFCondition
DT_1_SendPData(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)1347 DT_1_SendPData(PRIVATE_NETWORKKEY ** /*network*/,
1348          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
1349 {
1350     OFCondition cond = EC_Normal;
1351     DUL_PDVLIST
1352         * pdvList;
1353 
1354     /* Remember that params used to be a variable of type DUL_PDVLIST * and restore this variable */
1355     pdvList = (DUL_PDVLIST *) params;
1356 
1357     /* send the data which is contained in pdvList over the network */
1358     cond = sendPDataTCP(association, pdvList);
1359 
1360     /* and go to the next specified state */
1361     (*association)->protocolState = nextState;
1362 
1363     /* return return value */
1364     return cond;
1365 }
1366 
1367 /* DT_2_IndicatePData
1368 **
1369 ** Purpose:
1370 **      Send P-DATA indication primitive.
1371 **
1372 ** Parameter Dictionary:
1373 **
1374 **      network         Handle to the network environment
1375 **      association     Handle to the Association
1376 **      nextState       The next state to be reached from the current state
1377 **      params          Service parameters describing the Association
1378 **
1379 ** Return Values:
1380 **
1381 **
1382 ** Notes:
1383 **
1384 ** Algorithm:
1385 **      Description of the algorithm (optional) and any other notes.
1386 */
1387 
1388 static OFCondition
DT_2_IndicatePData(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1389 DT_2_IndicatePData(PRIVATE_NETWORKKEY ** /*network*/,
1390          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1391 {
1392     unsigned char
1393         pduType,
1394         pduReserved;
1395     unsigned long
1396         pduLength,
1397         pdvLength,
1398         pdvCount;
1399     long
1400         length;
1401     unsigned char
1402        *p;
1403 
1404     /* determine the finite state machine's next state */
1405     (*association)->protocolState = nextState;
1406 
1407     /* read PDU body information from the incoming socket stream. In case the incoming */
1408     /* PDU's header information has not yet been read, also read this information. */
1409     OFCondition cond = readPDUBody(association, DUL_BLOCK, 0,
1410                        (*association)->fragmentBuffer,
1411                        (*association)->fragmentBufferLength,
1412                        &pduType, &pduReserved, &pduLength);
1413 
1414     /* return error if there was one */
1415     if (cond.bad())
1416         return cond;
1417 
1418     /* count the amount of PDVs in the current PDU */
1419     length = pduLength;                     //set length to the PDU's length
1420     pdvCount = 0;                           //set counter variable to 0
1421     p = (*association)->fragmentBuffer;     //set p to the buffer which contains the PDU's PDVs
1422     while (length >= 4) {                   //as long as length is at least 4 (= a length field can be read)
1423         EXTRACT_LONG_BIG(p, pdvLength);     //determine the length of the current PDV (the PDV p points to)
1424         p += 4 + pdvLength;                 //move p so that it points to the next PDV (move p 4 bytes over the length field plus the amount of bytes which is captured in the PDV's length field (over presentation context.Id, message information header and data fragment))
1425         length -= 4 + pdvLength;            //update length (i.e. determine the length of the buffer which has not been evaluated yet.)
1426         pdvCount++;                         //increase counter by one, since we've found another PDV
1427 
1428         // There must be at least a presentation context ID and a message
1429         // control header (see below), else the calculation pdvLength - 2 below
1430         // will underflow.
1431         if (pdvLength < 2)
1432         {
1433            char buf[256];
1434            sprintf(buf, "PDV with invalid length %lu encountered. This probably indicates a malformed P DATA PDU.", pdvLength);
1435            return makeDcmnetCondition(DULC_ILLEGALPDULENGTH, OF_error, buf);
1436         }
1437     }
1438 
1439     /* if after having counted the PDVs the length variable does not equal */
1440     /* 0, the PDV lengths did not add up correctly. Something is fishy. */
1441     if (length != 0)
1442     {
1443        char buf[256];
1444        sprintf(buf, "PDV lengths don't add up correctly: %d PDVs. This probably indicates a malformed P-DATA PDU. PDU type is %02x.", (int)pdvCount, (unsigned int) pduType);
1445        return makeDcmnetCondition(DULC_ILLEGALPDU, OF_error, buf);
1446     }
1447 
1448     /* let the the association indicate how many PDVs are contained in the PDU */
1449     (*association)->pdvCount = (int)pdvCount;
1450 
1451     /* if at least one PDV is contained in the PDU, the association's pdvIndex has to be set to 0 */
1452     if (pdvCount > 0)
1453         (*association)->pdvIndex = 0;
1454     else
1455     {
1456         /* if there is no PDV contained in the PDU, the association's pdvIndex has to be set to -1 */
1457         (*association)->pdvIndex = -1;
1458 
1459         /* bugfix by wilkens 01/10/12: return error if PDU does not contain any PDVs: */
1460         /* Additionally, it is not allowed to have a PDU that does not contain any PDVs. */
1461         /* Hence, return an error (see DICOM standard (year 2000) part 8, section 9.3.1, */
1462         /* figure 9-2) (or the corresponding section in a later version of the standard) */
1463        char buf[256];
1464        sprintf(buf, "PDU without any PDVs encountered. In DT_2_IndicatePData.  This probably indicates a  malformed P DATA PDU." );
1465        return makeDcmnetCondition(DULC_ILLEGALPDU, OF_error, buf);
1466     }
1467 
1468     /* at this point we need to set the association's currentPDV variable so that it contains the data */
1469     /* of the next unprocessed PDV (currentPDV shall always contain the next unprocessed PDV's data.) */
1470 
1471     /* set the fragment buffer (the buffer which contains all PDVs of the current PDU) to a local variable */
1472     p = (*association)->fragmentBuffer;
1473 
1474     /* The variable (*association)->pdvPointer always points to the buffer */
1475     /* address where the information of the PDV which is represented by the */
1476     /* association's currentPDV variable can be found. Set this variable. */
1477     (*association)->pdvPointer = p;
1478 
1479     /* determine the value in the PDV length field of the next (unprocessed) PDV */
1480     EXTRACT_LONG_BIG(p, pdvLength);
1481 
1482     /* set the data fragment length in the association's currentPDV.fragmentLength variable */
1483     /* (we start now overwriting all the entries in (*association)->currentPDV). The actual */
1484     /* length of the data fragment of the next (unprocessed) PDV equals the above determined */
1485     /* length minus 1 byte (for the presentation context ID) and minus another byte (for the */
1486     /* message control header). */
1487     (*association)->currentPDV.fragmentLength = pdvLength - 2;
1488 
1489     /* set the presentation context ID value in the association's currentPDV.presentationContextID */
1490     /* variable. This value is 1 byte wide and contained in the 5th byte of p (the first four bytes */
1491     /* contain the PDV length value, the fifth byte the presentation context ID value) */
1492     (*association)->currentPDV.presentationContextID = p[4];
1493 
1494     /* now determine if the next (unprocessed) PDV contains the last fragment of a data set or DIMSE */
1495     /* command and if the next (unprocessed) PDV is a data set PDV or command PDV. This information */
1496     /* is captured in the 6th byte of p: */
1497     unsigned char u = p[5];
1498     if (u & 2)
1499         (*association)->currentPDV.lastPDV = OFTrue;            //if bit 1 of the message control header is 1, this fragment does contain the last fragment of a data set or command
1500     else
1501         (*association)->currentPDV.lastPDV = OFFalse;           //if bit 1 of the message control header is 0, this fragment does not contain the last fragment of a data set or command
1502 
1503     if (u & 1)
1504         (*association)->currentPDV.pdvType = DUL_COMMANDPDV;    //if bit 0 of the message control header is 1, this is a command PDV
1505     else
1506         (*association)->currentPDV.pdvType = DUL_DATASETPDV;    //if bit 0 of the message control header is 0, this is a data set PDV
1507 
1508     /* now assign the data fragment of the next (unprocessed) PDV to the association's */
1509     /* currentPDV.data variable. The fragment starts 6 bytes to the right of the address */
1510     /* p currently points to. */
1511     (*association)->currentPDV.data = p + 6;
1512 
1513     /* return ok */
1514     return DUL_PDATAPDUARRIVED;
1515 }
1516 
1517 
1518 /* AR_1_SendReleaseRQ
1519 **
1520 ** Purpose:
1521 **      Send A-RELEASE-RQ PDU.
1522 **
1523 ** Parameter Dictionary:
1524 **
1525 **      network         Handle to the network environment
1526 **      association     Handle to the Association
1527 **      nextState       The next state to be reached from the current state
1528 **      params          Service parameters describing the Association
1529 **
1530 ** Return Values:
1531 **
1532 **
1533 ** Notes:
1534 **
1535 ** Algorithm:
1536 **      Description of the algorithm (optional) and any other notes.
1537 */
1538 
1539 static OFCondition
AR_1_SendReleaseRQ(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1540 AR_1_SendReleaseRQ(PRIVATE_NETWORKKEY ** /* network */,
1541          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1542 {
1543     OFCondition cond = EC_Normal;
1544 
1545     cond = sendReleaseRQTCP(association);
1546     (*association)->protocolState = nextState;
1547     return cond;
1548 }
1549 
1550 /* AR_2_IndicateRelease
1551 **
1552 ** Purpose:
1553 **      Issue A-RELEASE indication primitive.
1554 **
1555 ** Parameter Dictionary:
1556 **
1557 **      network         Handle to the network environment
1558 **      association     Handle to the Association
1559 **      nextState       The next state to be reached from the current state
1560 **      params          Service parameters describing the Association
1561 **
1562 ** Return Values:
1563 **
1564 **
1565 ** Notes:
1566 **
1567 ** Algorithm:
1568 **      Description of the algorithm (optional) and any other notes.
1569 */
1570 static OFCondition
AR_2_IndicateRelease(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1571 AR_2_IndicateRelease(PRIVATE_NETWORKKEY ** /*network*/,
1572          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1573 {
1574     unsigned char
1575         buffer[128],
1576         pduType,
1577         pduReserve;
1578     unsigned long
1579         pduLength;
1580 
1581     /* Read remaining unimportant bytes of the A-RELEASE-RQ PDU */
1582     OFCondition cond = readPDUBody(association, DUL_BLOCK, 0, buffer, sizeof(buffer),
1583                        &pduType, &pduReserve, &pduLength);
1584     if (cond.bad())
1585         return cond;
1586 
1587     if (pduLength == 4)
1588     {
1589       unsigned long mode = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
1590       if ((*association)->modeCallback && !((mode & DUL_MAXPDUCOMPAT) ^ DUL_DULCOMPAT))
1591       {
1592         (*association)->modeCallback->callback(mode);
1593       }
1594     }
1595 
1596     (*association)->protocolState = nextState;
1597     return DUL_PEERREQUESTEDRELEASE;
1598 }
1599 
1600 /* AR_3_ConfirmRelease
1601 **
1602 ** Purpose:
1603 **      Issue A-RELEASE confirmation primitive and close transport connection.
1604 **
1605 ** Parameter Dictionary:
1606 **
1607 **      network         Handle to the network environment
1608 **      association     Handle to the Association
1609 **      nextState       The next state to be reached from the current state
1610 **      params          Service parameters describing the Association
1611 **
1612 ** Return Values:
1613 **
1614 **
1615 ** Notes:
1616 **
1617 ** Algorithm:
1618 **      Description of the algorithm (optional) and any other notes.
1619 */
1620 static OFCondition
AR_3_ConfirmRelease(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1621 AR_3_ConfirmRelease(PRIVATE_NETWORKKEY ** /*network*/,
1622          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1623 {
1624     unsigned char
1625         buffer[128],
1626         pduType,
1627         pduReserve;
1628     unsigned long
1629         pduLength;
1630 
1631     /* Read remaining unimportant bytes of the A-RELEASE-RSP PDU */
1632     OFCondition cond = readPDUBody(association, DUL_BLOCK, 0, buffer, sizeof(buffer),
1633                        &pduType, &pduReserve, &pduLength);
1634 
1635     closeTransport(association);
1636     (*association)->protocolState = nextState;
1637     return cond;
1638 }
1639 
1640 /* AR_4_SendReleaseRP
1641 **
1642 ** Purpose:
1643 **      Issue A-RELEASE-RP PDU and start ARTIM timer.
1644 **
1645 ** Parameter Dictionary:
1646 **
1647 **      network         Handle to the network environment
1648 **      association     Handle to the Association
1649 **      nextState       The next state to be reached from the current state
1650 **      params          Service parameters describing the Association
1651 **
1652 ** Return Values:
1653 **
1654 **
1655 ** Notes:
1656 **
1657 ** Algorithm:
1658 **      Description of the algorithm (optional) and any other notes.
1659 */
1660 
1661 static OFCondition
AR_4_SendReleaseRP(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1662 AR_4_SendReleaseRP(PRIVATE_NETWORKKEY ** /* network */,
1663          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1664 {
1665     OFCondition cond = EC_Normal;
1666 
1667     cond = sendReleaseRPTCP(association);
1668     (*association)->timerStart = time(NULL);
1669     (*association)->protocolState = nextState;
1670     return cond;
1671 }
1672 
1673 /* AR_5_StopARTTIMtimer
1674 **
1675 ** Purpose:
1676 **      Stop ARTIM timer.
1677 **
1678 ** Parameter Dictionary:
1679 **
1680 **      network         Handle to the network environment
1681 **      association     Handle to the Association
1682 **      nextState       The next state to be reached from the current state
1683 **      params          Service parameters describing the Association
1684 **
1685 ** Return Values:
1686 
1687 **
1688 ** Notes:
1689 **
1690 ** Algorithm:
1691 **      Description of the algorithm (optional) and any other notes.
1692 */
1693 
1694 static OFCondition
AR_5_StopARTIMtimer(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int,void *)1695 AR_5_StopARTIMtimer(PRIVATE_NETWORKKEY ** /*network*/,
1696          PRIVATE_ASSOCIATIONKEY ** association, int /*nextState*/, void * /*params*/)
1697 {
1698     (*association)->timerStart = 0;
1699     return EC_Normal;
1700 }
1701 
1702 /* AR_6_IndicatePData
1703 **
1704 ** Purpose:
1705 **      Issue P-DATA indication.
1706 **
1707 ** Parameter Dictionary:
1708 **
1709 **      network         Handle to the network environment
1710 **      association     Handle to the Association
1711 **      nextState       The next state to be reached from the current state
1712 **      params          Service parameters describing the Association
1713 **
1714 ** Return Values:
1715 **
1716 ** Notes:
1717 **
1718 ** Algorithm:
1719 **      Description of the algorithm (optional) and any other notes.
1720 */
1721 static OFCondition
AR_6_IndicatePData(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY **,int,void *)1722 AR_6_IndicatePData(PRIVATE_NETWORKKEY ** /*network*/,
1723          PRIVATE_ASSOCIATIONKEY ** /*association*/, int /*nextState*/, void * /*params*/)
1724 {
1725     return DUL_PDATAPDUARRIVED;
1726 }
1727 
1728 /* AR_7_SendPData
1729 **
1730 ** Purpose:
1731 **      Issue P-DATA-TF PDU
1732 **
1733 ** Parameter Dictionary:
1734 **
1735 **      network         Handle to the network environment
1736 **      association     Handle to the Association
1737 **      nextState       The next state to be reached from the current state
1738 **      params          Service parameters describing the Association
1739 **
1740 ** Return Values:
1741 **
1742 **
1743 ** Notes:
1744 **
1745 ** Algorithm:
1746 **      Description of the algorithm (optional) and any other notes.
1747 */
1748 static OFCondition
AR_7_SendPDATA(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void * params)1749 AR_7_SendPDATA(PRIVATE_NETWORKKEY ** /*network*/,
1750          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void *params)
1751 {
1752     OFCondition cond = EC_Normal;
1753     DUL_PDVLIST
1754         * pdvList;
1755 
1756     pdvList = (DUL_PDVLIST *) params;
1757     cond = sendPDataTCP(association, pdvList);
1758     (*association)->protocolState = nextState;
1759     return cond;
1760 }
1761 
1762 /* AR_8_IndicateARelease
1763 **
1764 ** Purpose:
1765 **      Issue A-RELEASE indication (release collision):
1766 **
1767 ** Parameter Dictionary:
1768 **
1769 **      network         Handle to the network environment
1770 **      association     Handle to the Association
1771 **      nextState       The next state to be reached from the current state
1772 **      params          Service parameters describing the Association
1773 **
1774 ** Return Values:
1775 **
1776 ** Notes:
1777 **
1778 ** Algorithm:
1779 **      Description of the algorithm (optional) and any other notes.
1780 */
1781 static OFCondition
AR_8_IndicateARelease(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int,void *)1782 AR_8_IndicateARelease(PRIVATE_NETWORKKEY ** /*network*/,
1783          PRIVATE_ASSOCIATIONKEY ** association, int /*nextState*/, void * /*params*/)
1784 {
1785 /*    if (strcmp((*association)->applicationType, AE_REQUESTOR) == 0) */
1786 
1787     unsigned char
1788         buffer[128],
1789         pduType,
1790         pduReserve;
1791     unsigned long
1792         pduLength;
1793 
1794     /* Read remaining unimportant bytes of the A-RELEASE-RQ PDU */
1795     OFCondition cond = readPDUBody(association, DUL_BLOCK, 0, buffer, sizeof(buffer),
1796                        &pduType, &pduReserve, &pduLength);
1797     if (cond.bad())
1798         return cond;
1799 
1800     if ((*association)->applicationFunction == DICOM_APPLICATION_REQUESTOR)
1801         (*association)->protocolState = STATE9;
1802     else
1803         (*association)->protocolState = STATE10;
1804 
1805     return DUL_PEERREQUESTEDRELEASE;
1806 }
1807 
1808 /* AR_9_SendAReleaseRP
1809 **
1810 ** Purpose:
1811 **      Send A-RELEASE-RP PDU
1812 **
1813 ** Parameter Dictionary:
1814 **
1815 **      network         Handle to the network environment
1816 **      association     Handle to the Association
1817 **      nextState       The next state to be reached from the current state
1818 **      params          Service parameters describing the Association
1819 **
1820 ** Return Values:
1821 **
1822 ** Notes:
1823 **
1824 ** Algorithm:
1825 **      Description of the algorithm (optional) and any other notes.
1826 */
1827 static OFCondition
AR_9_SendAReleaseRP(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1828 AR_9_SendAReleaseRP(PRIVATE_NETWORKKEY ** /* network */,
1829          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1830 {
1831     OFCondition cond = sendReleaseRPTCP(association);
1832     (*association)->protocolState = nextState;
1833     return cond;
1834 }
1835 
1836 /* AR_10_ConfirmRelease
1837 **
1838 ** Purpose:
1839 **      Issue A-RELEASE confirmation primitive.
1840 **
1841 ** Parameter Dictionary:
1842 **
1843 **      network         Handle to the network environment
1844 **      association     Handle to the Association
1845 **      nextState       The next state to be reached from the current state
1846 **      params          Service parameters describing the Association
1847 **
1848 ** Return Values:
1849 **
1850 ** Notes:
1851 **
1852 ** Algorithm:
1853 **      Description of the algorithm (optional) and any other notes.
1854 */
1855 static OFCondition
AR_10_ConfirmRelease(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1856 AR_10_ConfirmRelease(PRIVATE_NETWORKKEY ** /*network*/,
1857          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1858 {
1859     (*association)->protocolState = nextState;
1860     return EC_Normal;
1861 }
1862 
1863 
1864 /* AA_1_SendAbort
1865 **
1866 ** Purpose:
1867 **      Send A-ABORT PDU (service-user source) and start (or restart if
1868 **      already started) ARTIM timer.
1869 **
1870 ** Parameter Dictionary:
1871 **
1872 **      network         Handle to the network environment
1873 **      association     Handle to the Association
1874 **      nextState       The next state to be reached from the current state
1875 **      params          Service parameters describing the Association
1876 **
1877 ** Return Values:
1878 **
1879 ** Notes:
1880 **
1881 ** Algorithm:
1882 **      Description of the algorithm (optional) and any other notes.
1883 */
1884 
1885 static OFCondition
AA_1_SendAAbort(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1886 AA_1_SendAAbort(PRIVATE_NETWORKKEY ** /* network */,
1887          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1888 {
1889     DUL_ABORTITEMS abortItems;
1890 
1891     abortItems.result = 0x00;
1892     abortItems.source = DUL_ABORTSERVICEUSER;
1893     abortItems.reason = 0x00;
1894 
1895     OFCondition cond = sendAbortTCP(&abortItems, association);
1896     (*association)->protocolState = nextState;
1897     (*association)->timerStart = time(NULL);
1898     return cond;
1899 }
1900 
1901 /* AA_2_CloseTransport
1902 **
1903 ** Purpose:
1904 **      Close transport connection.
1905 **
1906 ** Parameter Dictionary:
1907 **
1908 **      network         Handle to the network environment
1909 **      association     Handle to the Association
1910 **      nextState       The next state to be reached from the current state
1911 **      params          Service parameters describing the Association
1912 **
1913 ** Return Values:
1914 
1915 **
1916 ** Notes:
1917 **
1918 ** Algorithm:
1919 **      Description of the algorithm (optional) and any other notes.
1920 */
1921 
1922 static OFCondition
AA_2_CloseTransport(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1923 AA_2_CloseTransport(PRIVATE_NETWORKKEY ** /*network*/,
1924          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1925 {
1926     (*association)->timerStart = 0;
1927     closeTransport(association);
1928     (*association)->protocolState = nextState;
1929     return EC_Normal;
1930 }
1931 
1932 /* AA_2_CloseTimeout
1933 **
1934 ** Purpose:
1935 **      Stop ARTIM timer.
1936 **
1937 ** Parameter Dictionary:
1938 **
1939 **      network         Handle to the network environment
1940 **      association     Handle to the Association
1941 **      nextState       The next state to be reached from the current state
1942 **      params          Service parameters describing the Association
1943 **
1944 ** Return Values:
1945 **
1946 ** Notes:
1947 **
1948 ** Algorithm:
1949 **      Description of the algorithm (optional) and any other notes.
1950 */
1951 static OFCondition
AA_2_CloseTimeout(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1952 AA_2_CloseTimeout(PRIVATE_NETWORKKEY ** /*network*/,
1953          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1954 {
1955     (*association)->timerStart = 0;
1956     closeTransport(association);
1957     (*association)->protocolState = nextState;
1958     return DUL_READTIMEOUT;
1959 }
1960 
1961 
1962 /* AA_3_IndicatePeerAborted
1963 **
1964 ** Purpose:
1965 **      If (service-user initiated abort)
1966 **         - issue A-ABORT indication and close transport connection
1967 **      otherwise (service provider initiated abort)
1968 **         - issue A-P-ABORT indication and close transport connection.
1969 **
1970 ** Parameter Dictionary:
1971 **
1972 **      network         Handle to the network environment
1973 **      association     Handle to the Association
1974 **      nextState       The next state to be reached from the current state
1975 **      params          Service parameters describing the Association
1976 **
1977 ** Return Values:
1978 **
1979 ** Notes:
1980 **
1981 ** Algorithm:
1982 **      Description of the algorithm (optional) and any other notes.
1983 */
1984 static OFCondition
AA_3_IndicatePeerAborted(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)1985 AA_3_IndicatePeerAborted(PRIVATE_NETWORKKEY ** /*network*/,
1986          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
1987 {
1988     unsigned char
1989         buffer[128],
1990         pduType,
1991         pduReserve;
1992     unsigned long
1993         pduLength;
1994 
1995     /* Read remaining unimportant bytes of the A-ABORT PDU */
1996     OFCondition cond = readPDUBody(association, DUL_BLOCK, 0, buffer, sizeof(buffer),
1997                        &pduType, &pduReserve, &pduLength);
1998     if (cond.bad()) return cond;
1999 
2000     if (pduLength == 4)
2001     {
2002       unsigned long mode = pduReserve << 24 | buffer[0] << 16 | buffer[1] << 8 | buffer[3];
2003       if ((*association)->modeCallback && !((mode & DUL_MAXPDUCOMPAT) ^ DUL_DULCOMPAT))
2004       {
2005         (*association)->modeCallback->callback(mode);
2006       }
2007     }
2008 
2009     closeTransport(association);
2010     (*association)->protocolState = nextState;
2011     return DUL_PEERABORTEDASSOCIATION;
2012 }
2013 
2014 
2015 /* AA_4_IndicateAPAbort
2016 **
2017 ** Purpose:
2018 **      Issue A-P-ABORT indication primitive.
2019 **
2020 ** Parameter Dictionary:
2021 **
2022 **      network         Handle to the network environment
2023 **      association     Handle to the Association
2024 **      nextState       The next state to be reached from the current state
2025 **      params          Service parameters describing the Association
2026 **
2027 ** Return Values:
2028 ** Notes:
2029 **
2030 ** Algorithm:
2031 **      Description of the algorithm (optional) and any other notes.
2032 */
2033 
2034 static OFCondition
AA_4_IndicateAPAbort(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)2035 AA_4_IndicateAPAbort(PRIVATE_NETWORKKEY ** /*network*/,
2036          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
2037 {
2038     closeTransport(association);
2039     (*association)->protocolState = nextState;
2040     return DUL_PEERABORTEDASSOCIATION;
2041 }
2042 
2043 
2044 /* AA_5_StopARTIMtimer
2045 **
2046 ** Purpose:
2047 **      Stop ARTIM timer.
2048 **
2049 ** Parameter Dictionary:
2050 **
2051 **      network         Handle to the network environment
2052 **      association     Handle to the Association
2053 **      nextState       The next state to be reached from the current state
2054 **      params          Service parameters describing the Association
2055 **
2056 ** Return Values:
2057 **
2058 ** Notes:
2059 **
2060 ** Algorithm:
2061 **      Description of the algorithm (optional) and any other notes.
2062 */
2063 
2064 static OFCondition
AA_5_StopARTIMtimer(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)2065 AA_5_StopARTIMtimer(PRIVATE_NETWORKKEY ** /*network*/,
2066          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
2067 {
2068     (*association)->timerStart = 0;
2069     (*association)->protocolState = nextState;
2070     return EC_Normal;
2071 }
2072 
2073 
2074 /* AA_6_IgnorePDU
2075 **
2076 ** Purpose:
2077 **      Ignore PDU
2078 **
2079 ** Parameter Dictionary:
2080 **
2081 **      network         Handle to the network environment
2082 **      association     Handle to the Association
2083 **      nextState       The next state to be reached from the current state
2084 **      params          Service parameters describing the Association
2085 **
2086 ** Return Values:
2087 **
2088 **
2089 ** Notes:
2090 **
2091 ** Algorithm:
2092 **      Description of the algorithm (optional) and any other notes.
2093 */
2094 
2095 
2096 static OFCondition
AA_6_IgnorePDU(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)2097 AA_6_IgnorePDU(PRIVATE_NETWORKKEY ** /*network*/,
2098          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
2099 {
2100     unsigned char
2101         buffer[1024],
2102         PDUType,
2103         PDUReserved;
2104     long
2105         PDULength;
2106     unsigned long
2107         l;
2108 
2109     (*association)->protocolState = nextState;
2110     OFCondition cond = readPDUHead(association, buffer, sizeof(buffer), DUL_NOBLOCK,
2111                     PRV_DEFAULTTIMEOUT, &PDUType, &PDUReserved, &l);
2112     if (cond.bad()) {
2113         return cond;
2114     }
2115     PDULength = l;
2116     while (PDULength > 0 && cond.good())
2117     {
2118         cond = defragmentTCP((*association)->connection,
2119                              DUL_NOBLOCK, (*association)->timerStart,
2120                    (*association)->timeout, buffer, sizeof(buffer), &l);
2121         if (cond.bad()) return cond;
2122         PDULength -= l;
2123     }
2124     return EC_Normal;
2125 }
2126 
2127 
2128 /* AA_7_State13SendAbort
2129 **
2130 ** Purpose:
2131 **      SendA-ABORT PDU
2132 **
2133 ** Parameter Dictionary:
2134 **
2135 **      network         Handle to the network environment
2136 **      association     Handle to the Association
2137 **      nextState       The next state to be reached from the current state
2138 **      params          Service parameters describing the Association
2139 **
2140 ** Return Values:
2141 **
2142 **
2143 ** Notes:
2144 **
2145 ** Algorithm:
2146 **      Description of the algorithm (optional) and any other notes.
2147 */
2148 
2149 static OFCondition
AA_7_State13SendAbort(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)2150 AA_7_State13SendAbort(PRIVATE_NETWORKKEY ** /* network */,
2151          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
2152 {
2153     DUL_ABORTITEMS abortItems;
2154 
2155     abortItems.result = 0x00;
2156     abortItems.source = DUL_ABORTSERVICEPROVIDER;
2157     abortItems.reason = DUL_ABORTUNEXPECTEDPDU;
2158 
2159     OFCondition cond = sendAbortTCP(&abortItems, association);
2160     (*association)->protocolState = nextState;
2161     return cond;
2162 }
2163 
2164 
2165 /* AA_8_UnrecognizedPDUSendAbort
2166 **
2167 ** Purpose:
2168 **      Send A-ABORT PDU (service provider source), issue an A-P-ABORT
2169 **      indication and start timer.
2170 **
2171 ** Parameter Dictionary:
2172 **
2173 **      network         Handle to the network environment
2174 **      association     Handle to the Association
2175 **      nextState       The next state to be reached from the current state
2176 **      params          Service parameters describing the Association
2177 **
2178 ** Return Values:
2179 **
2180 **
2181 ** Notes:
2182 **
2183 ** Algorithm:
2184 **      Description of the algorithm (optional) and any other notes.
2185 */
2186 static OFCondition
AA_8_UnrecognizedPDUSendAbort(PRIVATE_NETWORKKEY **,PRIVATE_ASSOCIATIONKEY ** association,int nextState,void *)2187 AA_8_UnrecognizedPDUSendAbort(PRIVATE_NETWORKKEY ** /* network */,
2188          PRIVATE_ASSOCIATIONKEY ** association, int nextState, void * /*params*/)
2189 {
2190     DUL_ABORTITEMS abortItems;
2191 
2192     abortItems.result = 0x00;
2193     abortItems.source = DUL_ABORTSERVICEPROVIDER;
2194     abortItems.reason = DUL_ABORTUNEXPECTEDPDU;
2195 
2196     OFCondition cond = sendAbortTCP(&abortItems, association);
2197     (*association)->protocolState = nextState;
2198     (*association)->timerStart = time(0);
2199     return cond;
2200 }
2201 
2202 /* requestAssociationTCP
2203 **
2204 ** Purpose:
2205 **      Request a TCP Association
2206 **
2207 ** Parameter Dictionary:
2208 **
2209 **      network         Handle to the network environment
2210 **      params          Service parameters describing the Association
2211 **      association     Handle to the Association
2212 **
2213 ** Return Values:
2214 **
2215 **
2216 ** Notes:
2217 **
2218 ** Algorithm:
2219 **      Description of the algorithm (optional) and any other notes.
2220 */
2221 
2222 
2223 static OFCondition
requestAssociationTCP(PRIVATE_NETWORKKEY ** network,DUL_ASSOCIATESERVICEPARAMETERS * params,PRIVATE_ASSOCIATIONKEY ** association)2224 requestAssociationTCP(PRIVATE_NETWORKKEY ** network,
2225                       DUL_ASSOCIATESERVICEPARAMETERS * params,
2226                       PRIVATE_ASSOCIATIONKEY ** association)
2227 {
2228     char node[128];
2229     int  port;
2230     OFSockAddr server;
2231 #ifdef _WIN32
2232     SOCKET s;
2233 #else
2234     int s;
2235 #endif
2236     struct linger sockarg;
2237 
2238     if (sscanf(params->calledPresentationAddress, "%[^:]:%d", node, &port) != 2)
2239     {
2240         char buf[1024];
2241         sprintf(buf,"Illegal service parameter: %s", params->calledPresentationAddress);
2242         return makeDcmnetCondition(DULC_ILLEGALSERVICEPARAMETER, OF_error, buf);
2243     }
2244 
2245     /*
2246      * At least officially, gethostbyname will not accept an IP address on many
2247      * operating systems (e.g. Windows or FreeBSD), so we need to explicitly
2248      * handle the IP address case.
2249      */
2250     unsigned long addr = inet_addr(node);
2251     if (addr != INADDR_NONE)
2252     {
2253         // it is an IPv4 address
2254         server.setFamily(AF_INET);
2255         struct sockaddr_in *sa = server.getSockaddr_in();
2256         sa->sin_addr.s_addr = addr;
2257     }
2258     else
2259     {
2260         // must be a host name or an IPv6 address
2261         OFStandard::getAddressByHostname(node, server);
2262         if (server.getFamily() == 0)
2263         {
2264           char buf2[4095]; // node could be a long string
2265           sprintf(buf2, "Attempt to connect to unknown host: %s", node);
2266           return makeDcmnetCondition(DULC_UNKNOWNHOST, OF_error, buf2);
2267         }
2268     }
2269     server.setPort(OFstatic_cast(unsigned short, htons(port)));
2270 
2271     // get global connection timeout
2272     Sint32 connectTimeout = dcmConnectionTimeout.get();
2273 
2274     s = socket(server.getFamily(), SOCK_STREAM, 0);
2275 #ifdef _WIN32
2276     if (s == INVALID_SOCKET)
2277 #else
2278     if (s < 0)
2279 #endif
2280     {
2281       OFString msg = "TCP Initialization Error: ";
2282       msg += OFStandard::getLastNetworkErrorCode().message();
2283       return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2284     }
2285 
2286 #ifdef HAVE_WINSOCK_H
2287     u_long arg = TRUE;
2288 #else
2289     int flags = 0;
2290 #endif
2291 
2292     if (connectTimeout >= 0)
2293     {
2294       // user has specified a timeout, switch socket to non-blocking mode
2295 #ifdef HAVE_WINSOCK_H
2296       ioctlsocket(s, FIONBIO, (u_long FAR *) &arg);
2297 #else
2298       flags = fcntl(s, F_GETFL, 0);
2299       fcntl(s, F_SETFL, O_NONBLOCK | flags);
2300 #endif
2301     }
2302 
2303     // depending on the socket mode, connect will block or return immediately
2304     int rc;
2305     do {
2306         rc = connect(s, server.getSockaddr(), server.size());
2307     } while (rc == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2308 
2309 #ifdef HAVE_WINSOCK_H
2310     if (rc == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
2311 #else
2312     if (rc < 0 && errno == EINPROGRESS)
2313 #endif
2314     {
2315 #ifndef DCMTK_HAVE_POLL
2316         // we're in non-blocking mode. Prepare to wait for timeout.
2317         fd_set fdSet;
2318         FD_ZERO(&fdSet);
2319 #ifdef __MINGW32__
2320         // on MinGW, FD_SET expects an unsigned first argument
2321         FD_SET((unsigned int) s, &fdSet);
2322 #else
2323         FD_SET(s, &fdSet);
2324 #endif /* __MINGW32__ */
2325 #endif /* DCMTK_HAVE_POLL */
2326 
2327         struct timeval timeout;
2328         timeout.tv_sec = connectTimeout;
2329         timeout.tv_usec = 0;
2330 
2331         do {
2332 #ifdef DCMTK_HAVE_POLL
2333             struct pollfd pfd[] =
2334             {
2335                 { s, POLLOUT, 0 }
2336             };
2337             rc = poll(pfd, 1, timeout.tv_sec*1000+(timeout.tv_usec/1000));
2338 #else
2339             // the typecast is safe because Windows ignores the first select() parameter anyway
2340             rc = select(OFstatic_cast(int, s + 1), NULL, &fdSet, NULL, &timeout);
2341 #endif
2342         } while (rc == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2343 
2344         if (DCM_dcmnetLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
2345         {
2346             DU_logSelectResult(rc);
2347         }
2348 
2349         // reset socket to blocking mode
2350 #ifdef HAVE_WINSOCK_H
2351         arg = FALSE;
2352         ioctlsocket(s, FIONBIO, (u_long FAR *) &arg);
2353 #else
2354         fcntl(s, F_SETFL, flags);
2355 #endif
2356         if (rc == 0)
2357         {
2358             // timeout reached, bail out with error return code
2359 #ifdef HAVE_WINSOCK_H
2360             (void) shutdown(s,  1 /* SD_SEND */);
2361             (void) closesocket(s);
2362 #else
2363             (void) close(s);
2364 #endif
2365             (*association)->networkState = NETWORK_DISCONNECTED;
2366             if ((*association)->connection) delete (*association)->connection;
2367             (*association)->connection = NULL;
2368 
2369             OFString msg = "TCP Initialization Error: ";
2370             msg += OFStandard::getLastNetworkErrorCode().message();
2371             msg += " (Timeout)";
2372             return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2373   }
2374 #ifndef HAVE_WINSOCK_H
2375         else if (rc > 0)
2376         {
2377             // select reports that our connection request has proceeded.
2378             // This could either mean success or an asynchronous error condition.
2379             // use getsockopt to check the socket status.
2380             int socketError = 0;
2381 
2382 #ifdef HAVE_DECLARATION_SOCKLEN_T
2383             // some platforms (e.g. Solaris 7) declare socklen_t
2384             socklen_t socketErrorLen = sizeof(socketError);
2385 #elif defined(HAVE_INTP_GETSOCKOPT)
2386             // some platforms (e.g. Solaris 2.5.1) prefer int
2387             int socketErrorLen = (int) sizeof(socketError);
2388 #else
2389             // some platforms (e.g. OSF1 4.0) prefer size_t
2390             size_t socketErrorLen = sizeof(socketError);
2391 #endif
2392 
2393             // Solaris 2.5.1 expects a char * as argument 4 of getsockopt. Most other
2394             // platforms expect void *, so casting to a char * should be safe.
2395             getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)(&socketError), &socketErrorLen);
2396             if (socketError)
2397             {
2398                 // asynchronous error on our socket, bail out.
2399                 (void) close(s);
2400                 (*association)->networkState = NETWORK_DISCONNECTED;
2401                 if ((*association)->connection) delete (*association)->connection;
2402                 (*association)->connection = NULL;
2403 
2404                 char buf[256];
2405                 OFString msg = "TCP Initialization Error: ";
2406                 msg += OFStandard::strerror(socketError, buf, sizeof(buf));
2407                 return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2408             }
2409         }
2410 #endif
2411     }
2412     else
2413     {
2414         // The connect() returned without using the select(), reset the socket if needed
2415         if (connectTimeout >= 0)
2416         {
2417             // reset socket to blocking mode
2418 #ifdef HAVE_WINSOCK_H
2419             arg = FALSE;
2420             ioctlsocket(s, FIONBIO, (u_long FAR *) &arg);
2421 #else
2422             fcntl(s, F_SETFL, flags);
2423 #endif
2424         }
2425     }
2426 
2427     if (rc < 0)
2428     {
2429         // an error other than timeout in non-blocking mode has occurred,
2430         // either in connect() or in select().
2431 #ifdef HAVE_WINSOCK_H
2432         (void) shutdown(s,  1 /* SD_SEND */);
2433         (void) closesocket(s);
2434 #else
2435         (void) close(s);
2436 #endif
2437         (*association)->networkState = NETWORK_DISCONNECTED;
2438         if ((*association)->connection) delete (*association)->connection;
2439         (*association)->connection = NULL;
2440 
2441         OFString msg = "TCP Initialization Error: ";
2442         msg += OFStandard::getLastNetworkErrorCode().message();
2443         return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2444     } else {
2445         // success - we've opened a TCP transport connection
2446 
2447         (*association)->networkState = NETWORK_CONNECTED;
2448         if ((*association)->connection) delete (*association)->connection;
2449 
2450         if (network && (*network) && ((*network)->networkSpecific.TCP.tLayer))
2451         {
2452           (*association)->connection = ((*network)->networkSpecific.TCP.tLayer)->createConnection(s, params->useSecureLayer);
2453         }
2454         else (*association)->connection = NULL;
2455 
2456         if ((*association)->connection == NULL)
2457         {
2458 #ifdef HAVE_WINSOCK_H
2459           (void) shutdown(s,  1 /* SD_SEND */);
2460           (void) closesocket(s);
2461 #else
2462           (void) close(s);
2463 #endif
2464           (*association)->networkState = NETWORK_DISCONNECTED;
2465 
2466           OFString msg = "TCP Initialization Error: ";
2467           msg += OFStandard::getLastNetworkErrorCode().message();
2468           return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2469         }
2470         sockarg.l_onoff = 0;
2471         sockarg.l_linger = 0;
2472 
2473         if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &sockarg, (int) sizeof(sockarg)) < 0)
2474         {
2475           OFString msg = "TCP Initialization Error: ";
2476           msg += OFStandard::getLastNetworkErrorCode().message();
2477           return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2478         }
2479         setTCPBufferLength(s);
2480 
2481         /*
2482          * Disable the so-called Nagle algorithm (if requested).
2483          * This might provide a better network performance on some systems/environments.
2484          * By default, the algorithm is not disabled unless DISABLE_NAGLE_ALGORITHM is defined.
2485          * The default behavior can be changed by setting the environment variable TCP_NODELAY.
2486          */
2487 
2488 #ifdef DONT_DISABLE_NAGLE_ALGORITHM
2489 #warning The macro DONT_DISABLE_NAGLE_ALGORITHM is not supported anymore. See "macros.txt" for details.
2490 #endif
2491 
2492 #ifdef DISABLE_NAGLE_ALGORITHM
2493         int tcpNoDelay = 1; // disable
2494 #else
2495         int tcpNoDelay = 0; // don't disable
2496 #endif
2497         char* tcpNoDelayString = NULL;
2498         DCMNET_TRACE("checking whether environment variable TCP_NODELAY is set");
2499         if ((tcpNoDelayString = getenv("TCP_NODELAY")) != NULL)
2500         {
2501           if (sscanf(tcpNoDelayString, "%d", &tcpNoDelay) != 1)
2502           {
2503             DCMNET_WARN("DULFSM: cannot parse environment variable TCP_NODELAY=" << tcpNoDelayString);
2504           }
2505         } else
2506           DCMNET_TRACE("  environment variable TCP_NODELAY not set, using the default value (" << tcpNoDelay << ")");
2507         if (tcpNoDelay) {
2508 #ifdef DISABLE_NAGLE_ALGORITHM
2509           DCMNET_DEBUG("DULFSM: disabling Nagle algorithm as defined at compilation time (DISABLE_NAGLE_ALGORITHM)");
2510 #else
2511           DCMNET_DEBUG("DULFSM: disabling Nagle algorithm as requested at runtime (TCP_NODELAY=" << tcpNoDelayString << ")");
2512 #endif
2513           if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&tcpNoDelay, sizeof(tcpNoDelay)) < 0)
2514           {
2515             OFString msg = "TCP Initialization Error: ";
2516             msg += OFStandard::getLastNetworkErrorCode().message();
2517             return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
2518           }
2519 #ifdef DISABLE_NAGLE_ALGORITHM
2520         } else {
2521           DCMNET_DEBUG("DULFSM: do not disable Nagle algorithm as requested at runtime (TCP_NODELAY=" << tcpNoDelayString << ")");
2522 #endif
2523         }
2524 
2525        DcmTransportLayerStatus tcsStatus;
2526        if (TCS_ok != (tcsStatus = (*association)->connection->clientSideHandshake()))
2527        {
2528          DCMNET_ERROR("TLS client handshake failed");
2529          OFString msg = "DUL secure transport layer: ";
2530          msg += (*association)->connection->errorString(tcsStatus);
2531          return makeDcmnetCondition(DULC_TLSERROR, OF_error, msg.c_str());
2532        }
2533        return EC_Normal;
2534     }
2535 }
2536 
2537 
2538 /* sendAssociationRQTCP
2539 **
2540 ** Purpose:
2541 **      Send a TCP association request PDU.
2542 **
2543 ** Parameter Dictionary:
2544 **
2545 **      network         Handle to the network environment
2546 **      params          Service parameters describing the Association
2547 **      association     Handle to the Association
2548 **
2549 ** Return Values:
2550 **
2551 **
2552 ** Notes:
2553 **
2554 ** Algorithm:
2555 **      Description of the algorithm (optional) and any other notes.
2556 */
2557 
2558 static OFCondition
sendAssociationRQTCP(PRIVATE_NETWORKKEY **,DUL_ASSOCIATESERVICEPARAMETERS * params,PRIVATE_ASSOCIATIONKEY ** association)2559 sendAssociationRQTCP(PRIVATE_NETWORKKEY ** /*network*/,
2560                      DUL_ASSOCIATESERVICEPARAMETERS * params,
2561                      PRIVATE_ASSOCIATIONKEY ** association)
2562 {
2563     PRV_ASSOCIATEPDU
2564     associateRequest;
2565     unsigned char
2566         buffer[4096],
2567        *b;
2568     unsigned long
2569         length;
2570     int
2571         nbytes;
2572 
2573     OFBitmanipTemplate<char>::zeroMem((char *)&associateRequest, sizeof(PRV_ASSOCIATEPDU)); // initialize PDU
2574     // associateRequest.presentationContextList = NULL;
2575     OFCondition cond = constructAssociatePDU(params, DUL_TYPEASSOCIATERQ,
2576                                  &associateRequest);
2577     if (cond.bad())
2578     {
2579         DCMNET_ERROR(cond.text());
2580         return cond;
2581     }
2582     if (associateRequest.length + 6 <= sizeof(buffer))
2583         b = buffer;
2584     else {
2585         b = (unsigned char*)malloc(size_t(associateRequest.length + 6));
2586         if (b == NULL)  return EC_MemoryExhausted;
2587     }
2588     cond = streamAssociatePDU(&associateRequest, b,
2589                               associateRequest.length + 6, &length);
2590 
2591     if ((*association)->associatePDUFlag)
2592     {
2593       // copy A-ASSOCIATE-RQ PDU
2594       (*association)->associatePDU = new char[length];
2595       if ((*association)->associatePDU)
2596       {
2597         memcpy((*association)->associatePDU, b, (size_t) length);
2598         (*association)->associatePDULength = length;
2599       }
2600     }
2601 
2602     destroyPresentationContextList(&associateRequest.presentationContextList);
2603     destroyUserInformationLists(&associateRequest.userInfo);
2604     if (cond.bad())
2605         return cond;
2606 
2607     do {
2608       nbytes = (*association)->connection ? (*association)->connection->write((char*)b, size_t(associateRequest.length + 6)) : 0;
2609     } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2610     if ((unsigned long) nbytes != associateRequest.length + 6)
2611     {
2612       OFString msg = "TCP I/O Error (";
2613       msg += OFStandard::getLastNetworkErrorCode().message();
2614       msg += ") occurred in routine: sendAssociationRQTCP";
2615       return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
2616     }
2617     if (b != buffer) free(b);
2618     return EC_Normal;
2619 }
2620 
2621 /* sendAssociationACTCP
2622 **
2623 ** Purpose:
2624 **      Send an Association ACK on a TCP connection
2625 **
2626 ** Parameter Dictionary:
2627 **
2628 **      network         Handle to the network environment
2629 **      params          Service parameters describing the Association
2630 **      association     Handle to the Association
2631 **
2632 ** Return Values:
2633 **
2634 **
2635 ** Algorithm:
2636 **      Description of the algorithm (optional) and any other notes.
2637 */
2638 
2639 static OFCondition
sendAssociationACTCP(PRIVATE_NETWORKKEY **,DUL_ASSOCIATESERVICEPARAMETERS * params,PRIVATE_ASSOCIATIONKEY ** association)2640 sendAssociationACTCP(PRIVATE_NETWORKKEY ** /*network*/,
2641                      DUL_ASSOCIATESERVICEPARAMETERS * params,
2642                      PRIVATE_ASSOCIATIONKEY ** association)
2643 {
2644     PRV_ASSOCIATEPDU
2645     associateReply;
2646     unsigned char
2647         buffer[4096],
2648        *b;
2649     unsigned long length = 0;
2650     int nbytes;
2651     DUL_ASSOCIATESERVICEPARAMETERS localService;
2652 
2653     OFBitmanipTemplate<char>::zeroMem((char *)&associateReply, sizeof(PRV_ASSOCIATEPDU)); // initialize PDU
2654     // associateReply.presentationContextList = NULL;
2655 
2656     localService = *params;
2657     OFCondition cond = constructAssociatePDU(&localService, DUL_TYPEASSOCIATEAC,
2658                                  &associateReply);
2659     if (cond.bad())
2660     {
2661         DCMNET_ERROR(cond.text());
2662         return cond;
2663     }
2664 
2665     // we need to have length+6 bytes in buffer, but 4 bytes reserve won't hurt
2666     if (associateReply.length + 10 <= sizeof(buffer)) b = buffer;
2667     else {
2668         b = (unsigned char*)malloc(size_t(associateReply.length + 10));
2669         if (b == NULL)  return EC_MemoryExhausted;
2670     }
2671     cond = streamAssociatePDU(&associateReply, b,
2672                               associateReply.length + 10, &length);
2673 
2674     if ((*association)->associatePDUFlag)
2675     {
2676       // copy A-ASSOCIATE-AC PDU
2677       (*association)->associatePDU = new char[length];
2678       if ((*association)->associatePDU)
2679       {
2680         memcpy((*association)->associatePDU, b, (size_t) length);
2681         (*association)->associatePDULength = length;
2682       }
2683     }
2684 
2685     destroyPresentationContextList(&associateReply.presentationContextList);
2686     destroyUserInformationLists(&associateReply.userInfo);
2687 
2688     if (cond.bad()) return cond;
2689 
2690     do {
2691       nbytes = (*association)->connection ? (*association)->connection->write((char*)b, size_t(associateReply.length + 6)) : 0;
2692     } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2693     if ((unsigned long) nbytes != associateReply.length + 6)
2694     {
2695       OFString msg = "TCP I/O Error (";
2696       msg += OFStandard::getLastNetworkErrorCode().message();
2697       msg += ") occurred in routine: sendAssociationACTCP";
2698       return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
2699     }
2700     if (b != buffer) free(b);
2701     return EC_Normal;
2702 }
2703 
2704 /* sendAssociationRJTCP
2705 **
2706 ** Purpose:
2707 **      Send an Association Reject PDU on a TCP connection.
2708 **
2709 ** Parameter Dictionary:
2710 **
2711 **      network         Handle to the network environment
2712 **      abortItems      Pointer to structure holding information regarding
2713 **                      the reason for rejection, the source and result.
2714 **      association     Handle to the Association
2715 **
2716 ** Return Values:
2717 **
2718 **
2719 ** Notes:
2720 **
2721 ** Algorithm:
2722 **      Description of the algorithm (optional) and any other notes.
2723 */
2724 
2725 static OFCondition
sendAssociationRJTCP(PRIVATE_NETWORKKEY **,DUL_ABORTITEMS * abortItems,PRIVATE_ASSOCIATIONKEY ** association)2726 sendAssociationRJTCP(PRIVATE_NETWORKKEY ** /*network*/,
2727          DUL_ABORTITEMS * abortItems, PRIVATE_ASSOCIATIONKEY ** association)
2728 {
2729 
2730     DUL_REJECTRELEASEABORTPDU
2731         pdu;
2732     unsigned char
2733         buffer[64],
2734        *b;
2735     unsigned long
2736         length;
2737     int
2738         nbytes;
2739 
2740 
2741     OFCondition cond = constructAssociateRejectPDU((unsigned char) abortItems->result,
2742         (unsigned char) abortItems->source, (unsigned char) abortItems->reason, &pdu);
2743     if (pdu.length + 6 <= sizeof(buffer))
2744         b = buffer;
2745     else {
2746         b = (unsigned char*)malloc(size_t(pdu.length + 6));
2747         if (b == NULL)  return EC_MemoryExhausted;
2748     }
2749     cond = streamRejectReleaseAbortPDU(&pdu, b, pdu.length + 6, &length);
2750 
2751     if ((*association)->associatePDUFlag)
2752     {
2753       // copy A-ASSOCIATE-RJ PDU
2754       (*association)->associatePDU = new char[length];
2755       if ((*association)->associatePDU)
2756       {
2757         memcpy((*association)->associatePDU, b, (size_t) length);
2758         (*association)->associatePDULength = length;
2759       }
2760     }
2761 
2762     if (cond.good())
2763     {
2764         do {
2765           nbytes = (*association)->connection ? (*association)->connection->write((char*)b, size_t(pdu.length + 6)) : 0;
2766         } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2767         if ((unsigned long) nbytes != pdu.length + 6)
2768         {
2769           OFString msg = "TCP I/O Error (";
2770           msg += OFStandard::getLastNetworkErrorCode().message();
2771           msg += ") occurred in routine: sendAssociationRJTCP";
2772           return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
2773         }
2774     }
2775     if (b != buffer) free(b);
2776     return cond;
2777 }
2778 
2779 /* sendAbortTCP
2780 **
2781 ** Purpose:
2782 **      Send an ABORT PDU over a TCP connection.
2783 **
2784 ** Parameter Dictionary:
2785 **      abortItems      Pointer to structure holding information regarding
2786 **                      the reason for rejection, the source and result.
2787 **      association     Handle to the Association
2788 **
2789 ** Return Values:
2790 **
2791 **
2792 ** Notes:
2793 **
2794 ** Algorithm:
2795 **      Description of the algorithm (optional) and any other notes.
2796 */
2797 
2798 static OFCondition
sendAbortTCP(DUL_ABORTITEMS * abortItems,PRIVATE_ASSOCIATIONKEY ** association)2799 sendAbortTCP(DUL_ABORTITEMS * abortItems,
2800              PRIVATE_ASSOCIATIONKEY ** association)
2801 {
2802     DUL_REJECTRELEASEABORTPDU
2803     pdu;
2804     unsigned char
2805         buffer[64],
2806        *b;
2807     unsigned long
2808         length;
2809     int
2810         nbytes;
2811 
2812     OFCondition cond = constructAbortPDU(abortItems->source, abortItems->reason, &pdu, (*association)->compatibilityMode);
2813     if (cond.bad())
2814         return cond;
2815 
2816     if (pdu.length + 6 <= sizeof(buffer))
2817         b = buffer;
2818     else {
2819         b = (unsigned char*)malloc(size_t(pdu.length + 6));
2820         if (b == NULL)  return EC_MemoryExhausted;
2821     }
2822     cond = streamRejectReleaseAbortPDU(&pdu, b, pdu.length + 6, &length);
2823     if (cond.good()) {
2824         do {
2825           nbytes = (*association)->connection ? (*association)->connection->write((char*)b, size_t(pdu.length + 6)) : 0;
2826         } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2827         if ((unsigned long) nbytes != pdu.length + 6)
2828         {
2829           OFString msg = "TCP I/O Error (";
2830           msg += OFStandard::getLastNetworkErrorCode().message();
2831           msg += ") occurred in routine: sendAbortTCP";
2832           return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
2833         }
2834     }
2835     if (b != buffer) free(b);
2836 
2837     return cond;
2838 }
2839 
2840 
2841 /* sendReleaseRQTCP
2842 **
2843 ** Purpose:
2844 **      Send a Release request PDU over a TCP connection
2845 **
2846 ** Parameter Dictionary:
2847 **
2848 **      association     Handle to the Association
2849 **
2850 ** Return Values:
2851 **
2852 **
2853 ** Notes:
2854 **
2855 ** Algorithm:
2856 **      Description of the algorithm (optional) and any other notes.
2857 */
2858 
2859 static OFCondition
sendReleaseRQTCP(PRIVATE_ASSOCIATIONKEY ** association)2860 sendReleaseRQTCP(PRIVATE_ASSOCIATIONKEY ** association)
2861 {
2862     DUL_REJECTRELEASEABORTPDU
2863     pdu;
2864     unsigned char
2865         buffer[64],
2866        *b;
2867     unsigned long
2868         length;
2869     int
2870         nbytes;
2871 
2872     OFCondition cond = constructReleaseRQPDU(&pdu, (*association)->compatibilityMode);
2873     if (cond.bad())
2874         return cond;
2875 
2876     if (pdu.length + 6 <= sizeof(buffer))
2877         b = buffer;
2878     else {
2879         b = (unsigned char*)malloc(size_t(pdu.length + 6));
2880         if (b == NULL)  return EC_MemoryExhausted;
2881     }
2882     cond = streamRejectReleaseAbortPDU(&pdu, b, pdu.length + 6, &length);
2883     if (cond.good()) {
2884         do {
2885           nbytes = (*association)->connection ? (*association)->connection->write((char*)b, size_t(pdu.length + 6)) : 0;
2886         } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2887         if ((unsigned long) nbytes != pdu.length + 6)
2888         {
2889           OFString msg = "TCP I/O Error (";
2890           msg += OFStandard::getLastNetworkErrorCode().message();
2891           msg += ") occurred in routine: sendReleaseRQTCP";
2892           return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
2893         }
2894     }
2895     if (b != buffer)
2896         free(b);
2897 
2898     return cond;
2899 }
2900 
2901 
2902 
2903 /* sendReleaseRPTCP
2904 **
2905 ** Purpose:
2906 **      Send a release response PDU over a TCP connection
2907 **
2908 ** Parameter Dictionary:
2909 **
2910 **      association     Handle to the Association
2911 **
2912 ** Return Values:
2913 **
2914 **
2915 ** Notes:
2916 **
2917 ** Algorithm:
2918 **      Description of the algorithm (optional) and any other notes.
2919 */
2920 
2921 static OFCondition
sendReleaseRPTCP(PRIVATE_ASSOCIATIONKEY ** association)2922 sendReleaseRPTCP(PRIVATE_ASSOCIATIONKEY ** association)
2923 {
2924     DUL_REJECTRELEASEABORTPDU
2925     pdu;
2926     unsigned char buffer[64],
2927        *b;
2928     unsigned long
2929         length;
2930     int
2931         nbytes;
2932 
2933     OFCondition cond = constructReleaseRPPDU(&pdu);
2934     if (cond.bad())
2935         return cond;
2936 
2937     if (pdu.length + 6 <= sizeof(buffer))
2938         b = buffer;
2939     else {
2940         b = (unsigned char*)malloc(size_t(pdu.length + 6));
2941         if (b == NULL)  return EC_MemoryExhausted;
2942     }
2943     cond = streamRejectReleaseAbortPDU(&pdu, b, pdu.length + 6, &length);
2944     if (cond.good()) {
2945         do {
2946           nbytes = (*association)->connection ? (*association)->connection->write((char*)b, size_t(pdu.length + 6)) : 0;
2947         } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
2948         if ((unsigned long) nbytes != pdu.length + 6)
2949         {
2950           OFString msg = "TCP I/O Error (";
2951           msg += OFStandard::getLastNetworkErrorCode().message();
2952           msg += ") occurred in routine: sendReleaseRPTCP";
2953           return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
2954         }
2955     }
2956     if (b != buffer) free(b);
2957 
2958     return cond;
2959 }
2960 
2961 
2962 /* SendPDataTCP
2963 **
2964 ** Purpose:
2965 **      Send a data PDU over a TCP connection.
2966 **
2967 ** Parameter Dictionary:
2968 **
2969 **      association     Handle to the Association
2970 **      pdvList         Pointer to structure holding a list of PDVs
2971 **
2972 ** Return Values:
2973 **
2974 **
2975 ** Notes:
2976 **
2977 ** Algorithm:
2978 **      Description of the algorithm (optional) and any other notes.
2979 */
2980 
2981 static OFCondition
sendPDataTCP(PRIVATE_ASSOCIATIONKEY ** association,DUL_PDVLIST * pdvList)2982 sendPDataTCP(PRIVATE_ASSOCIATIONKEY ** association,
2983              DUL_PDVLIST * pdvList)
2984 {
2985     DUL_PDV *pdv;
2986     unsigned long
2987         count,
2988         length,
2989         pdvLength,
2990         maxLength;
2991 
2992     OFBool localLast;
2993     unsigned char *p;
2994     DUL_DATAPDU dataPDU;
2995     OFBool firstTrip;
2996 
2997     /* assign the amount of PDVs in the array and the PDV array itself to local variables */
2998     count = pdvList->count;
2999     pdv = pdvList->pdv;
3000 
3001     /* determine the maximum size (length) of a PDU which can be sent over the network. */
3002     /* Note that the name "maxPDV" here is misleading. This field contains the maxPDU */
3003     /* size which is max PDV size +6 or max PDV data field + 12. */
3004     maxLength = (*association)->maxPDV;
3005 
3006     /* send the error indicator variable to ok */
3007     OFCondition cond = EC_Normal;
3008 
3009     /* adjust maxLength (maximum length of a PDU) */
3010     if (maxLength == 0) maxLength = ASC_MAXIMUMPDUSIZE - 12;
3011     else if (maxLength < 14)
3012     {
3013        char buf[256];
3014        sprintf(buf, "DUL Cannot send P-DATA PDU because receiver's max PDU size of %lu is illegal (must be > 12)", maxLength);
3015        cond = makeDcmnetCondition(DULC_ILLEGALPDULENGTH, OF_error, buf);
3016     }
3017     else maxLength -= 12;
3018 
3019     /* start a loop iterate over all PDVs in the given */
3020     /* list and send every PDVs data over the network */
3021     while (cond.good() && count > 0)
3022     {
3023         --count;
3024         /* determine length of PDV */
3025         length = pdv->fragmentLength;
3026         /* determine data to be set */
3027         p = (unsigned char *) pdv->data;
3028         /* because the current PDV's length can be greater than maxLength, we need */
3029         /* to start another loop so that we are able to send data gradually. So, */
3030         /* as long as this is the first iteration or length is greater than 0 and */
3031         /* at the same time no error occurred, do the following */
3032         firstTrip = OFTrue;
3033         while ((firstTrip || (length > 0)) && (cond.good())) {
3034             /* indicate that the first iteration has been executed */
3035             firstTrip = OFFalse;
3036             /* determine the length of the data fragment that will be sent */
3037             pdvLength = (length <= maxLength) ? length : maxLength;
3038             /* determine if the following fragment will be the last fragment to send */
3039             localLast = ((pdvLength == length) && pdv->lastPDV);
3040             /* construct a data PDU */
3041             cond = constructDataPDU(p, pdvLength, pdv->pdvType,
3042                            pdv->presentationContextID, localLast, &dataPDU);
3043             /* send the constructed PDU over the network */
3044             cond = writeDataPDU(association, &dataPDU);
3045 
3046             /* adjust the pointer to the data, so that he points to data which still has to be sent */
3047             p += pdvLength;
3048             /* adjust the length of the fragment which still has to be sent */
3049             length -= pdvLength;
3050         }
3051         /* advance to the next PDV */
3052         pdv++;
3053 
3054     }
3055     /* return corresponding result value */
3056     return cond;
3057 }
3058 
3059 /* writeDataPDU
3060 **
3061 ** Purpose:
3062 **      Send the data through the socket interface (for TCP).
3063 **
3064 ** Parameter Dictionary:
3065 **
3066 **      association     Handle to the Association
3067 **      pdu             The data unit that is to be sent thru the socket
3068 **
3069 ** Return Values:
3070 **
3071 **
3072 ** Notes:
3073 **
3074 ** Algorithm:
3075 **      Description of the algorithm (optional) and any other notes.
3076 */
3077 
3078 static OFCondition
writeDataPDU(PRIVATE_ASSOCIATIONKEY ** association,DUL_DATAPDU * pdu)3079 writeDataPDU(PRIVATE_ASSOCIATIONKEY ** association,
3080              DUL_DATAPDU * pdu)
3081 {
3082     unsigned char
3083         head[24];
3084     unsigned long
3085         length;
3086     int
3087         nbytes;
3088 
3089     /* construct a stream variable that will contain PDU head information */
3090     /* (in detail, this variable will contain PDU type, PDU reserved field, */
3091     /* PDU length, PDV length, presentation context ID, message control header) */
3092     /* (note that our representation of a PDU can only contain one PDV.) */
3093     OFCondition cond = streamDataPDUHead(pdu, head, sizeof(head), &length);
3094     if (cond.bad()) return cond;
3095 
3096     /* send the PDU head information (see above) */
3097     do
3098     {
3099       nbytes = (*association)->connection ? (*association)->connection->write((char*)head, size_t(length)) : 0;
3100     } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
3101 
3102     /* if not all head information was sent, return an error */
3103     if ((unsigned long) nbytes != length)
3104     {
3105         OFString msg = "TCP I/O Error (";
3106         msg += OFStandard::getLastNetworkErrorCode().message();
3107         msg += ") occurred in routine: writeDataPDU";
3108         return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
3109     }
3110 
3111     /* send the PDU's PDV data (note that our representation of a PDU can only contain one PDV.) */
3112     do
3113     {
3114       nbytes = (*association)->connection ? (*association)->connection->write((char*)pdu->presentationDataValue.data,
3115         size_t(pdu->presentationDataValue.length - 2)) : 0;
3116     } while (nbytes == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
3117 
3118         /* if not all head information was sent, return an error */
3119     if ((unsigned long) nbytes != pdu->presentationDataValue.length - 2)
3120     {
3121         OFString msg = "TCP I/O Error (";
3122         msg += OFStandard::getLastNetworkErrorCode().message();
3123         msg += ") occurred in routine: writeDataPDU";
3124         return makeDcmnetCondition(DULC_TCPIOERROR, OF_error, msg.c_str());
3125     }
3126 
3127     /* return ok */
3128     return EC_Normal;
3129 }
3130 
3131 /* closeTransport
3132 **
3133 ** Purpose:
3134 **      Close the transport connection.
3135 **
3136 ** Parameter Dictionary:
3137 **
3138 **      association     Handle to the Association
3139 **
3140 ** Return Values:
3141 **      None.
3142 **
3143 ** Notes:
3144 **
3145 ** Algorithm:
3146 **      Description of the algorithm (optional) and any other notes.
3147 */
3148 
3149 static void
closeTransport(PRIVATE_ASSOCIATIONKEY ** association)3150 closeTransport(PRIVATE_ASSOCIATIONKEY ** association)
3151 {
3152     closeTransportTCP(association);
3153 }
3154 
3155 /* closeTransportTCP
3156 **
3157 ** Purpose:
3158 **      Close the TCP transport connection.
3159 **
3160 ** Parameter Dictionary:
3161 **
3162 **      association     Handle to the Association
3163 **
3164 ** Return Values:
3165 **      None
3166 **
3167 ** Notes:
3168 **
3169 ** Algorithm:
3170 **      Description of the algorithm (optional) and any other notes.
3171 */
3172 
3173 static void
closeTransportTCP(PRIVATE_ASSOCIATIONKEY ** association)3174 closeTransportTCP(PRIVATE_ASSOCIATIONKEY ** association)
3175 {
3176   if ((*association)->connection)
3177   {
3178    (*association)->connection->close();
3179    delete (*association)->connection;
3180    (*association)->connection = NULL;
3181   }
3182 }
3183 
3184 
3185 /* clearPDUCache()
3186 **
3187 ** Purpose:
3188 **      Clear the Association of any PDUs.
3189 **
3190 ** Parameter Dictionary:
3191 **
3192 **      association     Handle to the Association
3193 **
3194 ** Return Values:
3195 **      None.
3196 **
3197 ** Notes:
3198 **
3199 ** Algorithm:
3200 **      Description of the algorithm (optional) and any other notes.
3201 */
3202 
3203 static void
clearPDUCache(PRIVATE_ASSOCIATIONKEY ** association)3204 clearPDUCache(PRIVATE_ASSOCIATIONKEY ** association)
3205 {
3206     (*association)->inputPDU = NO_PDU;
3207 }
3208 
3209 /* PRV_NextPDUType
3210 **
3211 ** Purpose:
3212 **      Get the next PDU's type.
3213 **
3214 ** Parameter Dictionary:
3215 **
3216 **      association     Handle to the Association
3217 **      block           Option for blocking/non-blocking
3218 **      timeout         Timeout interval within to get the Type of the next
3219 **                      PDU
3220 **      pduType         The type of next PDU returned to caller.
3221 **
3222 ** Return Values:
3223 **
3224 **
3225 ** Notes:
3226 **
3227 ** Algorithm:
3228 **      Description of the algorithm (optional) and any other notes.
3229 */
3230 
3231 OFCondition
PRV_NextPDUType(PRIVATE_ASSOCIATIONKEY ** association,DUL_BLOCKOPTIONS block,int timeout,unsigned char * pduType)3232 PRV_NextPDUType(PRIVATE_ASSOCIATIONKEY ** association, DUL_BLOCKOPTIONS block,
3233                 int timeout, unsigned char *pduType)
3234 {
3235     OFCondition cond = EC_Normal;
3236 
3237     /* if the association does not contain PDU header information, do something */
3238     if ((*association)->inputPDU == NO_PDU) {
3239         /* try to receive a PDU header on the network */
3240         cond = readPDUHead(association, (*association)->pduHead,
3241                            sizeof((*association)->pduHead),
3242                            block, timeout, &(*association)->nextPDUType,
3243                            &(*association)->nextPDUReserved,
3244                            &(*association)->nextPDULength);
3245         /* if receiving was not successful, return this error */
3246         if (cond.bad())
3247             return cond;
3248         /* indicate that the association does contain PDU header information */
3249         (*association)->inputPDU = PDU_HEAD;
3250     }
3251     /* determine PDU type and assign it to reference parameter */
3252     *pduType = (*association)->nextPDUType;
3253 
3254     /* return ok */
3255     return EC_Normal;
3256 }
3257 
3258 /* readPDU
3259 **
3260 ** Purpose:
3261 **      Read the PDU from the incoming stream.
3262 **
3263 ** Parameter Dictionary:
3264 **      association     Handle to the Association
3265 **      block           Blocking/non-blocking options for reading
3266 **      timeout         Timeout interval for reading
3267 **      buffer          Buffer holding the PDU (returned to the caller)
3268 **      PDUType         Type of the PDU (returned to the caller)
3269 **      PDUReserved     Reserved field of the PDU (returned to caller)
3270 **      PDULength       Length of PDU read (returned to caller)
3271 **
3272 **
3273 ** Return Values:
3274 **
3275 **
3276 ** Notes:
3277 **    The buffer is allocated (malloc) when the PDU size is known.
3278 **    If malloc fails, EC_MemoryExhausted is returned.
3279 **    Otherwise, the buffer must be released (free) by the caller!
3280 **
3281 **    This function is only used to receive incoming A-ASSOCIATE-RQ
3282 **    and A-ASSOCIATE-AC PDUs.
3283 **
3284 ** Algorithm:
3285 **      Description of the algorithm (optional) and any other notes.
3286 */
3287 
3288 static OFCondition
readPDU(PRIVATE_ASSOCIATIONKEY ** association,DUL_BLOCKOPTIONS block,int timeout,unsigned char ** buffer,unsigned char * pduType,unsigned char * pduReserved,unsigned long * pduLength)3289 readPDU(PRIVATE_ASSOCIATIONKEY ** association, DUL_BLOCKOPTIONS block,
3290         int timeout, unsigned char **buffer,
3291         unsigned char *pduType, unsigned char *pduReserved,
3292         unsigned long *pduLength)
3293 {
3294     OFCondition cond = EC_Normal;
3295     unsigned long maxLength;
3296 
3297     *buffer = NULL;
3298     if ((*association)->inputPDU == NO_PDU) {
3299         cond = readPDUHead(association, (*association)->pduHead,
3300                            sizeof((*association)->pduHead),
3301                            block, timeout, &(*association)->nextPDUType,
3302                            &(*association)->nextPDUReserved,
3303                            &(*association)->nextPDULength);
3304         if (cond.bad())
3305             return cond;
3306         (*association)->inputPDU = PDU_HEAD;
3307     }
3308 
3309     size_t limit = dcmAssociatePDUSizeLimit.get();
3310     if ((limit > 0) && ((*association)->nextPDULength > limit))
3311     {
3312       DCMNET_ERROR("A-ASSOCIATE PDU too large: " << (*association)->nextPDULength << " bytes, refusing." );
3313       return NET_EC_AssociatePDUTooLarge;
3314     }
3315 
3316     maxLength = ((*association)->nextPDULength)+100;
3317     *buffer = (unsigned char *)malloc(size_t(maxLength));
3318     if (*buffer)
3319     {
3320       (void) memcpy(*buffer, (*association)->pduHead, sizeof((*association)->pduHead));
3321       cond = readPDUBody(association, block, timeout,
3322         (*buffer) + sizeof((*association)->pduHead),
3323         maxLength - sizeof((*association)->pduHead),
3324         pduType, pduReserved, pduLength);
3325     } else cond = EC_MemoryExhausted;
3326     return cond;
3327 }
3328 
3329 
3330 /* readPDUHead
3331 **
3332 ** Purpose:
3333 **      Read the header from the PDU. The type of protocol is obtained from
3334 **      the Association handle.
3335 **
3336 ** Parameter Dictionary:
3337 **      association     Handle to the Association
3338 **      buffer          Buffer holding the PDU
3339 **      maxLength       Maximum allowable length of the header
3340 **      block           Blocking/non-blocking options for reading
3341 **      timeout         Timeout interval for reading
3342 **      PDUType         Type of the PDU (returned to the caller)
3343 **      PDUReserved     Reserved field of the PDU (returned to caller)
3344 **      PDULength       Length of PDU header read (returned to caller)
3345 **
3346 ** Return Values:
3347 **
3348 **
3349 ** Notes:
3350 **
3351 ** Algorithm:
3352 **      Description of the algorithm (optional) and any other notes.
3353 */
3354 static OFCondition
readPDUHead(PRIVATE_ASSOCIATIONKEY ** association,unsigned char * buffer,unsigned long maxLength,DUL_BLOCKOPTIONS block,int timeout,unsigned char * PDUType,unsigned char * PDUReserved,unsigned long * PDULength)3355 readPDUHead(PRIVATE_ASSOCIATIONKEY ** association,
3356             unsigned char *buffer, unsigned long maxLength,
3357             DUL_BLOCKOPTIONS block, int timeout,
3358             unsigned char *PDUType, unsigned char *PDUReserved,
3359             unsigned long *PDULength)
3360 {
3361     /* initialize return value */
3362     OFCondition cond = EC_Normal;
3363 
3364     /* if the association does not already contain PDU header */
3365     /* information, we need to try to receive a PDU on the network */
3366     if ((*association)->inputPDU == NO_PDU)
3367     {
3368         /* try to receive data */
3369         cond = readPDUHeadTCP(association, buffer, maxLength, block, timeout,
3370              &(*association)->nextPDUType, &(*association)->nextPDUReserved, &(*association)->nextPDULength);
3371     }
3372     /* if everything was ok */
3373     if (cond.good()) {
3374         /* indicate that the association does contain PDU header information */
3375         (*association)->inputPDU = PDU_HEAD;
3376         /* determine PDU type and the PDU's value in the reserved field and */
3377         /* in the PDU length field and assign it to reference parameter */
3378         *PDUType = (*association)->nextPDUType;
3379         *PDUReserved = (*association)->nextPDUReserved;
3380         *PDULength = (*association)->nextPDULength;
3381 
3382         /* check if the value in the length field of the PDU shows a legal value; */
3383         /* there is a maximum length for PDUs which shall be sent over the network. */
3384         /* the length of this PDU must not be greater than the specified maximum length. */
3385         /* (bugfix - thanks to B. Gorissen, Philips Medical Systems) */
3386         if ((*PDUType == DUL_TYPEDATA) && (*PDULength > (*association)->maxPDVInput))
3387         {
3388           char buf1[256];
3389           sprintf(buf1, "DUL Illegal PDU Length %ld.  Max expected %ld", *PDULength, (*association)->maxPDVInput);
3390           cond = makeDcmnetCondition(DULC_ILLEGALPDULENGTH, OF_error, buf1);
3391         }
3392     }
3393 
3394     /* return result value */
3395     return cond;
3396 }
3397 
3398 
3399 /* readPDUBody
3400 **
3401 ** Purpose:
3402 **      Read the body of the incoming PDU.
3403 **
3404 ** Parameter Dictionary:
3405 **      association     Handle to the Association
3406 **      block           For blocking/non-blocking read
3407 **      timeout         Timeout interval for reading
3408 **      buffer          Buffer to hold the PDU
3409 **      maxLength       MAximum number of bytes to read
3410 **      pduType         PDU Type of the incoming PDU (returned to caller)
3411 **      pduReserved     Reserved field in the PDU
3412 **      pduLength       Actual number of bytes read
3413 **
3414 ** Return Values:
3415 **
3416 **
3417 ** Notes:
3418 **
3419 ** Algorithm:
3420 **      Description of the algorithm (optional) and any other notes.
3421 */
3422 
3423 static OFCondition
readPDUBody(PRIVATE_ASSOCIATIONKEY ** association,DUL_BLOCKOPTIONS block,int timeout,unsigned char * buffer,unsigned long maxLength,unsigned char * pduType,unsigned char * pduReserved,unsigned long * pduLength)3424 readPDUBody(PRIVATE_ASSOCIATIONKEY ** association,
3425             DUL_BLOCKOPTIONS block, int timeout,
3426             unsigned char *buffer, unsigned long maxLength,
3427             unsigned char *pduType, unsigned char *pduReserved,
3428             unsigned long *pduLength)
3429 {
3430     OFCondition cond = EC_Normal;
3431 
3432     /* read PDU body information */
3433     cond = readPDUBodyTCP(association, block, timeout, buffer, maxLength,
3434                           pduType, pduReserved, pduLength);
3435 
3436     /* indicate that the association does not contain PDU information any more */
3437     /* (in detail we indicate that the association can be used to store new PDU */
3438     /* information, since the current PDU's information is available through */
3439     /* the variables pduType, pduReserved, pduLength, and buffer now. */
3440     (*association)->inputPDU = NO_PDU;
3441 
3442     /* return return value */
3443     return cond;
3444 }
3445 
3446 /* readPDUHeadTCP
3447 **
3448 ** Purpose:
3449 **      Read the TCP header.
3450 **
3451 ** Parameter Dictionary:
3452 **      association     Handle to the Association
3453 **      buffer          Buffer containing the header
3454 **      maxLength       Maximum length of the header
3455 **      block           For blocking/non-blocking read
3456 **      timeout         Timeout interval for reading
3457 **      type            One of the many DUL PDU types
3458 **      reserved        For reserved field
3459 **      pduLength       Length of the header finally read in.
3460 **
3461 ** Return Values:
3462 **
3463 **
3464 ** Notes:
3465 **
3466 ** Algorithm:
3467 **      Description of the algorithm (optional) and any other notes.
3468 */
3469 
3470 
3471 static OFCondition
readPDUHeadTCP(PRIVATE_ASSOCIATIONKEY ** association,unsigned char * buffer,unsigned long maxLength,DUL_BLOCKOPTIONS block,int timeout,unsigned char * type,unsigned char * reserved,unsigned long * pduLength)3472 readPDUHeadTCP(PRIVATE_ASSOCIATIONKEY ** association,
3473                unsigned char *buffer, unsigned long maxLength,
3474                DUL_BLOCKOPTIONS block, int timeout,
3475      unsigned char *type, unsigned char *reserved, unsigned long *pduLength)
3476 {
3477     unsigned long
3478         length;
3479     static const unsigned char
3480         legalPDUTypes[] = {
3481         DUL_TYPEASSOCIATERQ, DUL_TYPEASSOCIATEAC,
3482         DUL_TYPEASSOCIATERJ, DUL_TYPEDATA,
3483         DUL_TYPERELEASERQ, DUL_TYPERELEASERP,
3484         DUL_TYPEABORT
3485     };
3486     int
3487         found;
3488     unsigned long
3489         idx;
3490 
3491     /* check if the buffer is too small to capture the PDU head we are about to receive */
3492     if (maxLength < 6)
3493     {
3494         return makeDcmnetCondition(DULC_CODINGERROR, OF_error, "Coding Error in DUL routine: buffer too small in readPDUTCPHead");
3495     }
3496 
3497     /* (for non-blocking reading) if the timeout refers to */
3498     /* the default timeout, set timeout correspondingly */
3499     if (timeout == PRV_DEFAULTTIMEOUT)
3500         timeout = (*association)->timeout;
3501 
3502     /* try to receive PDU header (6 bytes) over the network, mind blocking */
3503     /* options; in the end, buffer will contain the 6 bytes that were read. */
3504     OFCondition cond = defragmentTCP((*association)->connection, block, (*association)->timerStart, timeout, buffer, 6, &length);
3505 
3506     /* if receiving was not successful, return the corresponding error value */
3507     if (cond.bad()) return cond;
3508 
3509     DCMNET_TRACE("Read PDU HEAD TCP:" << STD_NAMESPACE hex << STD_NAMESPACE setfill('0')
3510               << " " << STD_NAMESPACE setw(2) << (unsigned short)(buffer[0])
3511               << " " << STD_NAMESPACE setw(2) << (unsigned short)(buffer[1])
3512               << " " << STD_NAMESPACE setw(2) << (unsigned short)(buffer[2])
3513               << " " << STD_NAMESPACE setw(2) << (unsigned short)(buffer[3])
3514               << " " << STD_NAMESPACE setw(2) << (unsigned short)(buffer[4])
3515               << " " << STD_NAMESPACE setw(2) << (unsigned short)(buffer[5]));
3516 
3517     /* determine PDU type (captured in byte 0 of buffer) and assign it to reference parameter */
3518     *type = *buffer++;
3519 
3520     /* determine value in the PDU header's reserved field (captured */
3521     /* in byte 1 of buffer) and assign it to reference parameter */
3522     *reserved = *buffer++;
3523 
3524     /* check if the PDU which was received shows a legal PDU type */
3525     for (idx = found = 0; !found && idx < sizeof(legalPDUTypes); idx++) {
3526         found = (*type == legalPDUTypes[idx]);
3527     }
3528 
3529     /* if the PDU's type was not legal, return a corresponding message */
3530     if (!found)
3531     {
3532         char buf[256];
3533         sprintf(buf, "Unrecognized PDU type: %2x", *type);
3534         return makeDcmnetCondition(DULC_UNRECOGNIZEDPDUTYPE, OF_error, buf);
3535     }
3536 
3537     /* determine the value in the PDU length field of the PDU */
3538     /* which was received and assign it to reference parameter */
3539     EXTRACT_LONG_BIG(buffer, length);
3540     buffer += 4;
3541     *pduLength = length;
3542 
3543     DCMNET_TRACE("Read PDU HEAD TCP: type: "
3544               << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(2) << (unsigned short)(*type)
3545               << ", length: " << STD_NAMESPACE dec << (*pduLength) << " ("
3546               << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(2) << (unsigned int)*pduLength
3547               << ")");
3548 
3549     /* return ok */
3550     return EC_Normal;
3551 }
3552 
3553 
3554 /* readPDUBodyTCP
3555 **
3556 ** Purpose:
3557 **      Read the PDU from a TCP socket interface.
3558 **
3559 ** Parameter Dictionary:
3560 **      association     Handle to the Association
3561 **      block           For blocking/non-blocking read
3562 **      timeout         Timeout interval for reading
3563 **      buffer          Buffer to hold the PDU
3564 **      maxLength       Maximum number of bytes to read
3565 **      pduType         PDU Type of the incoming PDU (returned to caller)
3566 **      pduReserved     Reserved field in the PDU
3567 **      pduLength       Actual number of bytes read
3568 **
3569 **
3570 ** Return Values:
3571 **
3572 **
3573 ** Notes:
3574 **
3575 ** Algorithm:
3576 **      Description of the algorithm (optional) and any other notes.
3577 */
3578 
3579 static OFCondition
readPDUBodyTCP(PRIVATE_ASSOCIATIONKEY ** association,DUL_BLOCKOPTIONS block,int timeout,unsigned char * buffer,unsigned long maxLength,unsigned char * pduType,unsigned char * pduReserved,unsigned long * pduLength)3580 readPDUBodyTCP(PRIVATE_ASSOCIATIONKEY ** association,
3581                DUL_BLOCKOPTIONS block, int timeout,
3582                unsigned char *buffer, unsigned long maxLength,
3583                unsigned char *pduType, unsigned char *pduReserved,
3584                unsigned long *pduLength)
3585 {
3586     OFCondition cond = EC_Normal;
3587     unsigned long
3588         length;
3589 
3590     /* check if the association does not already contain PDU head information. */
3591     if ((*association)->inputPDU == NO_PDU) {
3592         /* If it does not, we need to try to receive PDU head information over the network */
3593         cond = readPDUHead(association, (*association)->pduHead,
3594                            sizeof((*association)->pduHead),
3595                            block, timeout, &(*association)->nextPDUType,
3596                            &(*association)->nextPDUReserved,
3597                            &(*association)->nextPDULength);
3598         /* return error if there was one */
3599         if (cond.bad())
3600             return cond;
3601     }
3602     /* determine PDU type and its values in the reserved field and in the PDU length field */
3603     *pduType = (*association)->nextPDUType;
3604     *pduReserved = (*association)->nextPDUReserved;
3605     *pduLength = (*association)->nextPDULength;
3606 
3607     /* (for non-blocking reading) if the timeout refers to */
3608     /* the default timeout, set timeout correspondingly */
3609     if (timeout == PRV_DEFAULTTIMEOUT)
3610         timeout = (*association)->timeout;
3611 
3612     /* bugfix by meichel 97/04/28: test if PDU fits into buffer */
3613     if (*pduLength > maxLength)
3614     {
3615       /* if it doesn't, set error indicator variable */
3616       cond = DUL_ILLEGALPDULENGTH;
3617     } else {
3618       /* if it does, read the current PDU's PDVs from the incoming socket stream. (Note that the */
3619       /* PDVs of the current PDU are (*association)->nextPDULength bytes long. Hence, in detail */
3620       /* we want to try to receive (*association)->nextPDULength bytes of data on the network) */
3621       /* The information that was received will be available through the buffer variable. */
3622       cond = defragmentTCP((*association)->connection,
3623                          block, (*association)->timerStart, timeout,
3624                          buffer, (*association)->nextPDULength, &length);
3625     }
3626 
3627     /* return result value */
3628     return cond;
3629 }
3630 
3631 
3632 /* defragmentTCP
3633 **
3634 ** Purpose:
3635 **      Actually read the desired number of bytes  of the PDU from the
3636 **      incoming socket stream.
3637 **
3638 ** Parameter Dictionary:
3639 **      sock            Socket descriptor
3640 **      block           Blocking/non-blocking option
3641 **      timerStart      Time at which the reading operation is started.
3642 **      timeout         Timeout interval for reading
3643 **      p               Buffer to hold the information
3644 **      l               Maximum number of bytes to read
3645 **      rtnLength       Actual number of bytes that were read (returned
3646 **                      to the caller)
3647 **
3648 **
3649 ** Return Values:
3650 **
3651 **
3652 ** Notes:
3653 **
3654 ** Algorithm:
3655 **      Description of the algorithm (optional) and any other notes.
3656 */
3657 
3658 
3659 static OFCondition
defragmentTCP(DcmTransportConnection * connection,DUL_BLOCKOPTIONS block,time_t timerStart,int timeout,void * p,unsigned long l,unsigned long * rtnLen)3660 defragmentTCP(DcmTransportConnection *connection, DUL_BLOCKOPTIONS block, time_t timerStart,
3661               int timeout, void *p, unsigned long l, unsigned long *rtnLen)
3662 {
3663     unsigned char *b;
3664     int bytesRead;
3665 
3666     /* assign buffer to local variable */
3667     b = (unsigned char *) p;
3668 
3669     /* if reference parameter is a valid pointer, initialize its value */
3670     if (rtnLen != NULL)
3671         *rtnLen = 0;
3672 
3673     /* if there is no network connection, return an error */
3674     if (connection == NULL) return DUL_NULLKEY;
3675 
3676     int timeToWait = 0;
3677     if (block == DUL_NOBLOCK)
3678     {
3679         /* figure out how long we want to wait: if timerStart equals 0 we want to wait exactly */
3680         /* timeout seconds starting from the call to select(...) within the below called function; */
3681         /* if timerStart does not equal 0 we want to subtract the time which has already passed */
3682         /* after the timer was started from timeout and wait the resulting amount of seconds */
3683         /* starting from the call to select(...) within the below called function. */
3684         if (timerStart == 0) timerStart = time(NULL);
3685     }
3686 
3687     /* start a loop: since we want to receive l bytes of data over the network, */
3688     /* we won't stop waiting for data until we actually did receive l bytes. */
3689     while (l > 0)
3690     {
3691         /* receive data from the network connection; wait until */
3692         /* we actually did receive data or an error occurred */
3693         do
3694         {
3695             /* if DUL_NOBLOCK is specified as a blocking option, we only want to wait a certain
3696              * time for receiving data over the network. If no data was received during that time,
3697              * DUL_READTIMEOUT shall be returned. Note that if DUL_BLOCK is specified the application
3698              * will not stop waiting until data is actually received over the network.
3699              */
3700             if (block == DUL_NOBLOCK)
3701             {
3702                 /* determine remaining time to wait */
3703                 timeToWait = timeout - (int) (time(NULL) - timerStart);
3704 
3705                 /* go ahead and see if within timeout seconds data will be received over the network. */
3706                 /* if not, return DUL_READTIMEOUT, if yes, stay in this function. */
3707                 if (!connection->networkDataAvailable(timeToWait)) return DUL_READTIMEOUT;
3708             }
3709 
3710             /* data has become available, now call read(). */
3711             bytesRead = connection->read((char*)b, size_t(l));
3712 
3713         } while (bytesRead == -1 && OFStandard::getLastNetworkErrorCode().value() == DCMNET_EINTR);
3714 
3715         /* if we actually received data, move the buffer pointer to its own end, update the variable */
3716         /* that determines the end of the first loop, and update the reference parameter return variable */
3717         if (bytesRead > 0) {
3718             b += bytesRead;
3719             l -= (unsigned long) bytesRead;
3720             if (rtnLen != NULL)
3721                 *rtnLen += (unsigned long) bytesRead;
3722         } else {
3723             /* in case we did not receive any data, an error must have occurred; return a corresponding result value */
3724             return DUL_NETWORKCLOSED;
3725         }
3726     }
3727     return EC_Normal;
3728 }
3729 
3730 /* dump_pdu
3731 **
3732 ** Purpose:
3733 **      Debugging routine for dumping a pdu
3734 **
3735 ** Parameter Dictionary:
3736 **      type            PDU type
3737 **      buffer          Buffer holding the PDU
3738 **      length          Length of the PDU
3739 **
3740 ** Return Values:
3741 **      None
3742 **
3743 ** Notes:
3744 **
3745 ** Algorithm:
3746 **      Description of the algorithm (optional) and any other notes.
3747 */
3748 
3749 static OFString
dump_pdu(const char * type,void * buffer,unsigned long length)3750 dump_pdu(const char *type, void *buffer, unsigned long length)
3751 {
3752     unsigned char
3753        *p;
3754     int
3755         position = 0;
3756     OFOStringStream
3757         str;
3758 
3759     str << "PDU Type: " << type << ", PDU Length: " << length-6 << " + 6 bytes PDU header" << OFendl;
3760     if (length > 512) {
3761         str << "Only dumping 512 bytes." << OFendl;
3762         length = 512;
3763     }
3764     p = (unsigned char*)buffer;
3765 
3766     while (length-- > 0) {
3767         str << "  "
3768             << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(2) << ((unsigned int)(*p++))
3769             << STD_NAMESPACE dec;
3770         if ((++position) % 16 == 0) str << OFendl;
3771     }
3772     str << OFStringStream_ends;
3773     OFSTRINGSTREAM_GETOFSTRING(str, ret)
3774     return ret;
3775 }
3776 
3777 
3778 
3779 /* setTCPBufferLength
3780 **
3781 ** Purpose:
3782 **      This routine checks for the existence of an environment
3783 **      variable (TCP_BUFFER_LENGTH).  If that variable is defined (and
3784 **      is a legal integer), this routine sets the socket SNDBUF and RCVBUF
3785 **      variables to the value defined in TCP_BUFFER_LENGTH.
3786 **
3787 ** Parameter Dictionary:
3788 **      sock            Socket descriptor (identifier)
3789 **
3790 ** Return Values:
3791 **      None
3792 **
3793 ** Notes:
3794 **
3795 ** Algorithm:
3796 **      Description of the algorithm (optional) and any other notes.
3797 */
3798 
3799 #ifdef _WIN32
setTCPBufferLength(SOCKET sock)3800 static void setTCPBufferLength(SOCKET sock)
3801 #else
3802 static void setTCPBufferLength(int sock)
3803 #endif
3804 {
3805     char *TCPBufferLength;
3806     int bufLen;
3807 
3808     /*
3809      * check whether environment variable TCP_BUFFER_LENGTH is set.
3810      * If not, the the operating system is responsible for selecting
3811      * appropriate values for the TCP send and receive buffer lengths.
3812      */
3813     DCMNET_TRACE("checking whether environment variable TCP_BUFFER_LENGTH is set");
3814     if ((TCPBufferLength = getenv("TCP_BUFFER_LENGTH")) != NULL) {
3815         if (sscanf(TCPBufferLength, "%d", &bufLen) == 1) {
3816 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
3817             if (bufLen == 0)
3818                 bufLen = 65536; // a socket buffer size of 64K gives good throughput for image transmission
3819             DCMNET_DEBUG("DULFSM: setting TCP buffer length to " << bufLen << " bytes");
3820             (void) setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &bufLen, sizeof(bufLen));
3821             (void) setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &bufLen, sizeof(bufLen));
3822 #else
3823             DCMNET_WARN("DULFSM: setTCPBufferLength: cannot set TCP buffer length socket option: "
3824                 << "code disabled because SO_SNDBUF and SO_RCVBUF constants are unknown");
3825 #endif // SO_SNDBUF and SO_RCVBUF
3826         } else
3827             DCMNET_WARN("DULFSM: cannot parse environment variable TCP_BUFFER_LENGTH=" << TCPBufferLength);
3828     } else
3829         DCMNET_TRACE("  environment variable TCP_BUFFER_LENGTH not set, using the system defaults");
3830 }
3831 
3832 /* translatePresentationContextList
3833 **
3834 ** Purpose:
3835 **      Translate the internal list into a user context list and a
3836 **      SCU-SCP role list
3837 **
3838 ** Parameter Dictionary:
3839 **      internalList            Input list from which the two output lists
3840 **                              are derived
3841 **      SCUSCPRoleList          Role list (returned to the caller)
3842 **      userContextList         User context list (returned to the caller)
3843 **
3844 ** Return Values:
3845 **
3846 ** Notes:
3847 **
3848 ** Algorithm:
3849 **      Description of the algorithm (optional) and any other notes.
3850 */
3851 OFCondition
translatePresentationContextList(LST_HEAD ** internalList,LST_HEAD ** SCUSCPRoleList,LST_HEAD ** userContextList)3852 translatePresentationContextList(LST_HEAD ** internalList,
3853                                  LST_HEAD ** SCUSCPRoleList,
3854                                  LST_HEAD ** userContextList)
3855 {
3856     PRV_PRESENTATIONCONTEXTITEM
3857         * context;
3858     DUL_PRESENTATIONCONTEXT
3859         * userContext;
3860     DUL_SUBITEM
3861         * subItem;
3862     DUL_TRANSFERSYNTAX
3863         * transfer;
3864     PRV_SCUSCPROLE
3865         * scuscpRole;
3866     OFCondition cond = EC_Normal;
3867 
3868     context = (PRV_PRESENTATIONCONTEXTITEM*)LST_Head(internalList);
3869     (void) LST_Position(internalList, (LST_NODE*)context);
3870     while (context != NULL) {
3871         userContext = (DUL_PRESENTATIONCONTEXT*)malloc(sizeof(DUL_PRESENTATIONCONTEXT));
3872         if (userContext == NULL) return EC_MemoryExhausted;
3873         if ((userContext->proposedTransferSyntax = LST_Create()) == NULL) return EC_MemoryExhausted;
3874 
3875         userContext->acceptedTransferSyntax[0] = '\0';
3876         userContext->presentationContextID = context->contextID;
3877         OFStandard::strlcpy(userContext->abstractSyntax, context->abstractSyntax.data, sizeof(userContext->abstractSyntax));
3878         userContext->proposedSCRole = DUL_SC_ROLE_DEFAULT;
3879         userContext->acceptedSCRole = DUL_SC_ROLE_DEFAULT;
3880 
3881         scuscpRole = findSCUSCPRole(SCUSCPRoleList,
3882                                     userContext->abstractSyntax);
3883         if (scuscpRole != NULL) {
3884             if (scuscpRole->SCURole == scuscpRole->SCPRole) {
3885                 userContext->proposedSCRole = DUL_SC_ROLE_SCUSCP;
3886                 if (scuscpRole->SCURole == 0)
3887                     DCMNET_WARN("DULFSM: both role fields are 0 in SCP/SCU role selection sub-item");
3888             }
3889             else if (scuscpRole->SCURole == 1)
3890                 userContext->proposedSCRole = DUL_SC_ROLE_SCU;
3891             else  // SCPRole == 1
3892                 userContext->proposedSCRole = DUL_SC_ROLE_SCP;
3893         }
3894         subItem = (DUL_SUBITEM*)LST_Head(&context->transferSyntaxList);
3895         if (subItem == NULL)
3896         {
3897             char buf1[256];
3898             sprintf(buf1, "DUL Peer supplied illegal number of transfer syntaxes (%d)", 0);
3899             free(userContext);
3900             return makeDcmnetCondition(DULC_PEERILLEGALXFERSYNTAXCOUNT, OF_error, buf1);
3901         }
3902         (void) LST_Position(&context->transferSyntaxList, (LST_NODE*)subItem);
3903         while (subItem != NULL) {
3904             transfer = (DUL_TRANSFERSYNTAX*)malloc(sizeof(DUL_TRANSFERSYNTAX));
3905             if (transfer == NULL) return EC_MemoryExhausted;
3906             OFStandard::strlcpy(transfer->transferSyntax, subItem->data, sizeof(transfer->transferSyntax));
3907 
3908             LST_Enqueue(&userContext->proposedTransferSyntax, (LST_NODE*)transfer);
3909             subItem = (DUL_SUBITEM*)LST_Next(&context->transferSyntaxList);
3910         }
3911         LST_Enqueue(userContextList, (LST_NODE*)userContext);
3912         context = (PRV_PRESENTATIONCONTEXTITEM*)LST_Next(internalList);
3913     }
3914     return EC_Normal;
3915 }
3916 
3917 /* findPresentationCtx
3918 **
3919 ** Purpose:
3920 **      Find the requested presentation context using the contextID as the
3921 **      key
3922 **
3923 ** Parameter Dictionary:
3924 **      list            List to be searched
3925 **      contextID       The search key
3926 **
3927 ** Return Values:
3928 **      A presentation context list (if found) else NULL.
3929 **
3930 ** Algorithm:
3931 **      Description of the algorithm (optional) and any other notes.
3932 */
3933 DUL_PRESENTATIONCONTEXT *
findPresentationCtx(LST_HEAD ** lst,DUL_PRESENTATIONCONTEXTID contextID)3934 findPresentationCtx(
3935                     LST_HEAD ** lst, DUL_PRESENTATIONCONTEXTID contextID)
3936 {
3937     DUL_PRESENTATIONCONTEXT
3938     * ctx;
3939 
3940     if ((ctx = (DUL_PRESENTATIONCONTEXT*)LST_Head(lst)) == NULL)
3941         return NULL;
3942 
3943     (void) LST_Position(lst, (LST_NODE*)ctx);
3944     while (ctx != NULL) {
3945         if (ctx->presentationContextID == contextID)
3946             return ctx;
3947 
3948         ctx = (DUL_PRESENTATIONCONTEXT*)LST_Next(lst);
3949     }
3950     return NULL;
3951 }
3952 
3953 
3954 /* findSCUSCPRole
3955 **
3956 ** Purpose:
3957 **      Search for a SCUSCP role list, given the abstract syntax as the
3958 **      key.
3959 **
3960 ** Parameter Dictionary:
3961 **      list            List to be searched
3962 **      abstractSyntax  The search key
3963 **
3964 ** Return Values:
3965 **      The role list, if found, else NULL
3966 **
3967 ** Notes:
3968 **
3969 ** Algorithm:
3970 **      Description of the algorithm (optional) and any other notes.
3971 */
3972 PRV_SCUSCPROLE
3973 *
findSCUSCPRole(LST_HEAD ** lst,char * abstractSyntax)3974 findSCUSCPRole(LST_HEAD ** lst, char *abstractSyntax)
3975 {
3976     PRV_SCUSCPROLE
3977         * role;
3978 
3979     role = (PRV_SCUSCPROLE*)LST_Head(lst);
3980     if (role != NULL)
3981         (void) LST_Position(lst, (LST_NODE*)role);
3982 
3983     while (role != NULL) {
3984         if (strcmp(role->SOPClassUID, abstractSyntax) == 0)
3985             return role;
3986 
3987         role = (PRV_SCUSCPROLE*)LST_Next(lst);
3988     }
3989     return NULL;
3990 }
3991 
3992 void
destroyPresentationContextList(LST_HEAD ** l)3993 destroyPresentationContextList(LST_HEAD ** l)
3994 {
3995     PRV_PRESENTATIONCONTEXTITEM
3996     * prvCtx;
3997     DUL_SUBITEM
3998         * subItem;
3999 
4000     if (*l == NULL)
4001         return;
4002 
4003     prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Dequeue(l);
4004     while (prvCtx != NULL) {
4005         subItem = (DUL_SUBITEM*)LST_Dequeue(&prvCtx->transferSyntaxList);
4006         while (subItem != NULL) {
4007             free(subItem);
4008             subItem = (DUL_SUBITEM*)LST_Dequeue(&prvCtx->transferSyntaxList);
4009         }
4010         LST_Destroy(&prvCtx->transferSyntaxList);
4011         free(prvCtx);
4012         prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Dequeue(l);
4013     }
4014     LST_Destroy(l);
4015 }
4016 
4017 void
destroyUserInformationLists(DUL_USERINFO * userInfo)4018 destroyUserInformationLists(DUL_USERINFO * userInfo)
4019 {
4020     PRV_SCUSCPROLE
4021     * role;
4022 
4023     role = (PRV_SCUSCPROLE*)LST_Dequeue(&userInfo->SCUSCPRoleList);
4024     while (role != NULL) {
4025         free(role);
4026         role = (PRV_SCUSCPROLE*)LST_Dequeue(&userInfo->SCUSCPRoleList);
4027     }
4028     LST_Destroy(&userInfo->SCUSCPRoleList);
4029 
4030     /* extended negotiation */
4031     delete userInfo->extNegList; userInfo->extNegList = NULL;
4032 
4033     /* user identity negotiation */
4034     delete userInfo->usrIdent; userInfo->usrIdent = NULL;
4035 }
4036