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