source: BMXNET_RPMS_dotNET_UTILITIES-BMX/branch/IHS BMX Framework/IndianHealthService.BMXNet/Net/BMXNetSessionSocketConnection.cs@ 1193

Last change on this file since 1193 was 1193, checked in by Sam Habiel, 13 years ago

BMXNetEhrSessionConnection.cs implements ConnectionEncoding so that the project can compile.
BMXNetEhrSessionConnection.cs: ReceiveString TCP code refactored, AGAIN!

Updated dlls.

File size: 22.9 KB
Line 
1using System;
2using System.Diagnostics;
3using System.Text;
4using System.IO;
5using System.Net.Sockets;
6using System.Net;
7using System.Security.Cryptography;
8using System.Security.Permissions;
9using System.Security.Principal;
10using System.Threading;
11using System.Timers;
12using System.Linq;
13
14namespace IndianHealthService.BMXNet.Net
15{
16 /// <summary>
17 /// BMXNetBroker implements low-level socket connectivity to RPMS databases.
18 /// The VA RPC Broker must be running on the RPMS server in order for
19 /// BMXNetBroker to connect.
20 /// </summary>
21 [DnsPermission(SecurityAction.Assert, Unrestricted = true)]
22 internal class BMXNetSessionSocketConnection : BMXNetSessionConnection
23 {
24 public static int DefaultSendTimeout = 20000;
25 public static int DefaultReceiveTimeout = 40000;
26
27
28 private int _sendTimeout = 0;
29
30 public override int SendTimeout
31 {
32 get
33 {
34 if (_sendTimeout != 0)
35 {
36 return _sendTimeout;
37 }
38 return this.ConnectionSpec.SendTimeout == 0 ? DefaultSendTimeout : this.ConnectionSpec.SendTimeout;
39 }
40 set { _sendTimeout = value; }
41 }
42
43
44 private int _receiveTimeout = 0;
45
46 /// <summary>
47 /// Set and retrieve the timeout, in milliseconds, to receive a response from the RPMS server.
48 /// If the retrieve time exceeds the timeout, an exception will be thrown and the connection will be closed.
49 /// The default is 30 seconds.
50 /// </summary>
51 public override int ReceiveTimeout
52 {
53 get
54 {
55 if (_receiveTimeout != 0)
56 {
57 return _receiveTimeout;
58 }
59 return this.ConnectionSpec.ReceiveTimeout == 0 ? DefaultReceiveTimeout : this.ConnectionSpec.ReceiveTimeout;
60 }
61 set
62 {
63 _receiveTimeout = value;
64 }
65 }
66
67 public override Encoding ConnectionEncoding // Default Encoding determined by Windows; typically a Windows Code Page
68 {
69 get;
70 set;
71 }
72
73
74 public BMXNetSessionSocketConnection(BMXNetBroker aBroker):base(aBroker)
75 {
76 m_sWKID = "BMX";
77 m_sWINH = "";
78 m_sPRCH = "";
79 m_sWISH = "";
80 m_cHDR = ADEBHDR(m_sWKID,m_sWINH,m_sPRCH,m_sWISH);
81
82 ConnectionEncoding = Encoding.Default;
83
84 }
85
86
87
88 #region RPX Fields
89
90 private string m_sWKID;
91 private string m_sWISH;
92 private string m_sPRCH;
93 private string m_sWINH;
94 private string m_cHDR;
95 private bool m_bConnected;
96 private TcpClient m_pCommSocket;
97 private string m_sNameSpace = "";
98
99 #endregion RPX Fields
100
101
102 public TcpClient Socket
103 {
104 get { return this.m_pCommSocket; }
105 }
106
107 /// <summary>
108 /// Returns index of first instance of sSubString in sString.
109 /// If sSubString not found, returns -1.
110 /// </summary>
111 /// <returns></returns>
112
113 [SocketPermissionAttribute(SecurityAction.Assert,
114 Access = "Connect",
115 Host = "All",
116 Port = "All",
117 Transport = "All")]
118 protected virtual void OpenConnectionCommon()
119 {
120 try
121 {
122 TcpClient connector = null;
123
124 try
125 {
126 connector = new TcpClient();
127 connector.SendTimeout = this.SendTimeout;
128 connector.ReceiveTimeout = this.ReceiveTimeout;
129 connector.Connect(this.ServerAddress, this.ServerPort);
130 }
131 catch (SocketException exSocket)
132 {
133 string s = exSocket.Message + exSocket.StackTrace;
134 throw new BMXNetException(s);
135 }
136
137 //Prepare & send the connect message
138 string cSend = "TCPconnect^" + m_sNameSpace + "^^";
139 int nLen = cSend.Length;
140 string sLen = nLen.ToString();
141 sLen = sLen.PadLeft(5, '0');
142 cSend = "{BMX}" + sLen + cSend;
143
144 NetworkStream ns = connector.GetStream();
145 byte[] sendBytes = Encoding.ASCII.GetBytes(cSend);
146 ns.Write(sendBytes,0,sendBytes.Length);
147
148 m_pCommSocket = connector;
149 return;
150
151 }
152 catch (BMXNetException bmxEx)
153 {
154 throw bmxEx;
155 }
156 catch (Exception ex)
157 {
158 string s = ex.Message + ex.StackTrace;
159 throw new BMXNetException(s);
160 }
161 }//End OpenConnectionCommon
162
163
164 private BMXNetSocketConnectionSpec _connectionSpec = null;
165
166 internal BMXNetSocketConnectionSpec ConnectionSpec
167 {
168 get { return _connectionSpec; }
169 set { _connectionSpec = value; }
170 }
171
172
173 [SocketPermissionAttribute(SecurityAction.Assert,
174 Access = "Connect",
175 Host = "All",
176 Port = "All",
177 Transport = "All")]
178 public virtual bool OpenConnection(BMXNetSocketConnectionSpec aSpec)
179 {
180 this.ConnectionSpec = aSpec;
181
182 try
183 {
184 m_bConnected = false;
185 this.OpenConnectionCommon();
186 if (this.CheckConnection())
187 {
188 bool authenicated = false;
189
190 if (aSpec.UseWindowsAuthentication)
191 {
192 authenicated = this.SendSecurityRequest(aSpec.WindowsIdentity);
193 }
194 else
195 {
196 authenicated = SendSecurityRequest(aSpec.EncryptedAccessVerifyCode);
197 }
198
199 this.m_bConnected = authenicated;
200
201
202
203 int integerDuz = 0;
204 if (!int.TryParse(this.DUZ, out integerDuz))
205 {
206 throw new BMXNetException("Invalid DUZ, Unable to authenticate user.");
207 }
208
209 this.UserName = this.GetUserName();
210 }
211 return this.IsConnected;
212 }
213 catch (BMXNetException bmxEx)
214 {
215 throw bmxEx;
216 }
217 catch (Exception ex)
218 {
219 string s = ex.Message + ex.StackTrace;
220 throw new BMXNetException(s);
221 }
222 finally
223 {
224 if (!this.IsConnected)
225 {
226 this.PrimitiveCloseConnection();
227 }
228 }
229 }
230
231 bool m_bLogging = false;
232
233 private static void Log(String logMessage, TextWriter w)
234 {
235 w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
236 DateTime.Now.ToLongDateString());
237 w.WriteLine(" :");
238 w.WriteLine(" :{0}", logMessage);
239 w.WriteLine("-------------------------------");
240 // Update the underlying file.
241 w.Flush();
242 }
243
244 private bool CheckConnection()
245 {
246 try
247 {
248 String cMSG = ADEBLDMsg(m_cHDR, "BMX CONNECT STATUS", "");
249#if DEBUG
250 _watch = new Stopwatch();
251 _watch.Start();
252#endif
253 this.SendString(this.Socket, cMSG);
254 String strReceive = ReceiveString(this.Socket);
255#if DEBUG
256 _watch.Stop();
257
258 Debug.WriteLine("Time: " + _watch.ElapsedMilliseconds + " ms");
259 Debug.WriteLine("---");
260 _watch = null;
261#endif
262
263 String port = BMXNetBroker.Piece(strReceive, "|", 1);
264 String message = BMXNetBroker.Piece(strReceive, "|", 2);
265
266 if (!message.Contains("OK"))
267 {
268 throw new BMXNetException(message.Length==0 ? strReceive : message);
269 }
270
271 this.Job = BMXNetBroker.Piece(strReceive, "|", 3);
272
273 return true;
274 }
275 catch (Exception potentialProblem)
276 { //If there's an issue, assume old BMX 2.x/3.x so this feature is not supports BMX CONNECT STATUS
277 if (potentialProblem.Message.Contains("BMX CONNECT STATUS"))
278 {
279 return true;
280 }
281 else
282 {
283 throw potentialProblem;
284 }
285 }
286 }
287
288 public override bool IsConnected
289 {
290 get
291 {
292 return this.m_bConnected;
293 }
294 }
295
296 Stopwatch _watch = new Stopwatch();
297
298 protected override String SendReceiveString(string cSendString, string cMult)
299 {
300#if DEBUG
301 _watch = new Stopwatch();
302 _watch.Start();
303#endif
304 this.SendString(this.m_pCommSocket, cSendString, cMult);
305 Debug.WriteLine("Time After Send: " + _watch.ElapsedMilliseconds + " ms");
306 string _received = this.ReceiveString(this.m_pCommSocket);
307#if DEBUG
308 _watch.Stop();
309
310 Debug.WriteLine("Time: " + _watch.ElapsedMilliseconds + " ms");
311 Debug.WriteLine("---");
312 _watch = null;
313#endif
314 return _received;
315 }
316
317
318 protected virtual void SendString(TcpClient tcpClient, string cSendString)
319 {
320 string sMult = "";
321 SendString(tcpClient, cSendString, sMult);
322 }
323
324 protected virtual void SendString(TcpClient tcpClient, string cSendString, string cMult)
325 {
326#if DEBUG
327 Debug.WriteLine("Sending (T:" + Thread.CurrentThread.ManagedThreadId + "): " + cSendString);
328#endif
329 String encodedString = this.EncodeSendString(cSendString, cMult);
330
331 NetworkStream ns = tcpClient.GetStream();
332 ns.WriteTimeout = this.SendTimeout;
333 byte[] sendBytes = ConnectionEncoding.GetBytes(encodedString);
334 ns.Write(sendBytes,0,sendBytes.Length);
335 if (this.m_bLogging == true)
336 {
337 Log("Sent: " + cSendString, this.m_LogWriter);
338 }
339 return;
340 }
341
342
343 private string ReceiveString(TcpClient tcpClient)
344 {
345#if DEBUG
346 Debug.WriteLine("Time At Start of Receive: " + _watch.ElapsedMilliseconds + " ms");
347#endif
348
349 /******************INIT*****************/
350 NetworkStream ns = tcpClient.GetStream();
351 ns.ReadTimeout = this.ReceiveTimeout; //Timeout; throw exception automatically if we time out.
352
353 /*************SECURITY S-PACKET READ***********************/
354 int secPackLen = ns.ReadByte(); //Read S-Pack Length Byte
355 byte[] secPackContents = new byte[secPackLen];;
356 if (secPackLen > 0)
357 {
358 ns.Read(secPackContents, 0, secPackLen);
359 }
360
361 /****************ERROR S-PACKET READ******************/
362 int errPackLen = ns.ReadByte(); //Read S-Pack Length Byte
363 byte[] errPackConents = new byte[errPackLen];;
364 if (errPackLen > 0)
365 {
366 ns.Read(errPackConents, 0, errPackLen);
367 }
368
369 //If either one of these exists, then we have an error on the Mumps Database
370 if (secPackLen > 0 || errPackLen > 0)
371 {
372 //We still have to read the Data to empty the tcp OS buffers even if we have security/errors in the DB
373 while (ns.DataAvailable) ns.ReadByte() ; //while loop to empty the buffer -- this is theoretically slow, but that's not important here
374
375 //Now decode them.
376 string secString = ConnectionEncoding.GetString(secPackContents);
377 string errString = ConnectionEncoding.GetString(errPackConents);
378 throw new BMXNetException("Mumps Database Security/Error: " + secString + " | " + errString);
379 }
380
381 /*****************DATA STREAM READ*******************/
382 //Data stream from VISTA ends when we find the EOT (ASCII 4) character
383 MemoryStream mStream = new MemoryStream(1500); // Auto expanding memory storage for what we receive
384 int numberOfBytesRead = 0;
385 bool bFinished = false; // Have we found the End of Transmission (ASCII 4) yet?
386
387 // Main Read Loop
388 while (!bFinished) //while we are not done
389 {
390 byte[] bReadBuffer = new byte[1500]; //buffer size = MTU. Message MUST be smaller.
391 numberOfBytesRead = ns.Read(bReadBuffer, 0, bReadBuffer.Length); //read data
392 bFinished = FindChar(bReadBuffer, (char)4) > -1; //look for the EOT (ASCII 4) character
393 if (!bFinished) mStream.Write(bReadBuffer, 0, numberOfBytesRead); //if we are not done, put the whole stream in
394 else mStream.Write(bReadBuffer, 0, numberOfBytesRead - 1); //otherwise, number of bytes minus the $C(4) at the end
395 }
396
397 //At this point, we are done reading from the Network Stream. Now we have to decode the data.
398
399 //Decode the entire message.
400 string sReadBuffer = ConnectionEncoding.GetString(mStream.ToArray()); //decode
401
402 String decodedReceiveString = this.DecodeReceiveString(sReadBuffer);
403
404 if (this.m_bLogging)
405 {
406 Log("Received: " + decodedReceiveString, this.m_LogWriter);
407 }
408#if DEBUG
409 Debug.WriteLine("Time At End of Receive: " + _watch.ElapsedMilliseconds + " ms");
410 Debug.WriteLine("Received(T:" + Thread.CurrentThread.ManagedThreadId + "): " + decodedReceiveString.Replace((char)30, (char)10));
411#endif
412 return decodedReceiveString;
413
414
415 /* OLD CODE
416 NetworkStream ns = tcpClient.GetStream();
417 ns.ReadTimeout = this.ReceiveTimeout;
418
419
420 //TAE: This following is suspect. NetworkSTream Read and ReadTimeout provide
421 //the same behavior. Look at removing in the futuer. For now, this works.
422 int cyclePause = 25;
423 int cycles = 0;
424 DateTime start = DateTime.Now;
425 while (ns.DataAvailable == false)
426 {
427 if (cycles++ > 999)
428 break;
429 if ((DateTime.Now-start).TotalMilliseconds > this.ReceiveTimeout)
430 break;
431 Thread.Sleep(cyclePause);
432 }
433
434 Debug.Assert(ns.DataAvailable);
435 if (!ns.DataAvailable)
436 {
437 this.Close();
438 throw new Exception("BMXNetBroker.ReceiveString timeout. Connection Closed.");
439 }
440
441 byte[] bReadBuffer = new byte[1024];
442 string sReadBuffer = "";
443 StringBuilder sbAll = new StringBuilder("", 1024);
444 int numberOfBytesRead = 0;
445
446 // Incoming message may be larger than the buffer size.
447
448 bool bFinished = false;
449 bool bStarted = false;
450 int lpBuf = 0;
451 string sError = "";
452 string sAppError = "";
453 do
454 {
455
456 numberOfBytesRead = ns.Read(bReadBuffer, 0, bReadBuffer.Length);
457 if ((numberOfBytesRead == 1)&&(bStarted == false))
458 {
459 //TAE: This following is suspect. If Read is blocking then this sleep is extra.
460 //This is rarely called
461 Thread.Sleep(15);
462 numberOfBytesRead += ns.Read(bReadBuffer,1, bReadBuffer.Length-1);
463 }
464 if (bStarted == false)
465 {
466 //Process error info at beginning of returned string
467 int nErrLen = bReadBuffer[0];
468 int nAppLen = bReadBuffer[bReadBuffer[0]+1];
469 if ((bReadBuffer[2] == 0)&&(bReadBuffer[3] == 0))
470 { //special case: M error trap invoked in SND^XWBTCPC
471 lpBuf += 2;
472 }
473 sError = Encoding.ASCII.GetString(bReadBuffer, lpBuf + 1, nErrLen);
474 if (sError != "")
475 {
476 sAppError = Encoding.ASCII.GetString(bReadBuffer, lpBuf + 1 + nErrLen + 1, nAppLen);
477 throw new BMXNetException(sError);
478 }
479 sAppError = Encoding.ASCII.GetString(bReadBuffer, lpBuf+1+nErrLen+1, nAppLen);
480 lpBuf += (nErrLen + nAppLen + 2);
481 numberOfBytesRead -= (nErrLen + nAppLen + 2);
482 bStarted = true;
483 }
484
485 bFinished = FindChar(bReadBuffer, (char)4) > -1;
486 Debug.Assert(numberOfBytesRead > -1);
487 sReadBuffer = Encoding.ASCII.GetString(bReadBuffer, lpBuf, numberOfBytesRead);
488 lpBuf = 0;
489 if (bFinished)
490 {
491 sbAll.Append(sReadBuffer, 0, numberOfBytesRead -1);
492 }
493 else
494 {
495 sbAll.Append(sReadBuffer);
496 }
497 }
498 while(!bFinished);
499
500 String decodedReceiveString = this.DecodeReceiveString(sbAll.ToString());
501
502 if (this.m_bLogging)
503 {
504 Log("Received: " + decodedReceiveString, this.m_LogWriter);
505 }
506#if DEBUG
507 Debug.WriteLine("Time At End of Receive: " + _watch.ElapsedMilliseconds + " ms");
508#endif
509 return decodedReceiveString;
510 */
511 }
512
513
514 private bool SendSecurityRequest(WindowsIdentity winIdentity)
515 {
516 string strReceive = "";
517 string cMSG;
518 string sTest;
519
520 //Build AV Call
521 cMSG = ADEBLDMsg(m_cHDR, "BMX AV CODE", winIdentity.Name);
522#if DEBUG
523 _watch = new Stopwatch();
524 _watch.Start();
525#endif
526 SendString(this.Socket, cMSG);
527 strReceive = ReceiveString(this.Socket);
528#if DEBUG
529 _watch.Stop();
530
531 Debug.WriteLine("Time: " + _watch.ElapsedMilliseconds + " ms");
532 Debug.WriteLine("---");
533 _watch = null;
534#endif
535
536 sTest = strReceive.Substring(0,3);
537
538 char[] cDelim = {(char) 13,(char) 10,(char) 0};
539 string sDelim = new string(cDelim);
540 int nPiece = 1;
541 this.DUZ = M.Piece(strReceive, sDelim , nPiece);
542 if ((this.DUZ.Length==0 ) || ("0".Equals(this.DUZ)))
543 {
544 nPiece = 7;
545 string sReason = M.Piece(strReceive, sDelim, nPiece);
546 throw new Exception(sReason);
547 }
548
549 return true;
550 }
551
552 public String EncryptAccessVerifyCode(string anAccessCode, string aVerifyCode)
553 {
554 string accessVerifyPair = anAccessCode.ToUpper() + ";" + aVerifyCode.ToUpper();
555 return this.EncryptionProvider.Encrypt(accessVerifyPair);
556 }
557
558 private bool SendSecurityRequest(string encryptedAccessVerifyCode)
559 {
560 string strReceive = "";
561 string cMSG;
562
563 cMSG = ADEBLDMsg(m_cHDR, "XUS AV CODE", encryptedAccessVerifyCode);
564#if DEBUG
565 _watch = new Stopwatch();
566 _watch.Start();
567#endif
568 SendString(this.Socket, cMSG);
569 strReceive = ReceiveString(this.Socket);
570#if DEBUG
571 _watch.Stop();
572
573 Debug.WriteLine("Time: " + _watch.ElapsedMilliseconds + " ms");
574 Debug.WriteLine("---");
575 _watch = null;
576#endif
577
578 if (strReceive.StartsWith("M ERROR="))
579 {
580 this.DUZ = "0";
581 throw new BMXNetException("Server error has occured: " + strReceive);
582 }
583
584
585 char[] cDelim = {(char) 13,(char) 10,(char) 0};
586 string U = new string(cDelim);
587
588 this.DUZ = M.Piece(strReceive,U, 1);
589 String resultCode = M.Piece(strReceive, U, 2);
590 String resultMessageIndex = M.Piece(strReceive, U, 3);
591//R(0)=DUZ if sign-on was OK, zero if not OK.
592 //R(1)=(0=OK, 1,2...=Can't sign-on for some reason).
593 //R(2)=verify needs changing.
594 //R(3)=Message.
595 //R(4)=0
596 //R(5)=count of the number of lines of text, zero if none.
597
598 //extracall but same code-path as winidentity login
599
600 if ((this.DUZ == "0") || (this.DUZ == ""))
601 {
602 string sReason = M.Piece(strReceive, U, 7);
603 throw new BMXNetException(sReason);
604 }
605
606 return true;
607 }
608
609
610 public override void Close()
611 {
612 if (m_bConnected)
613 {
614 this.PrimitiveCloseConnection();
615 }
616 }
617
618 protected void PrimitiveCloseConnection() {
619
620 if (this.Socket != null)
621 {
622 SendString(this.Socket, "#BYE#");
623 this.Socket.Close();
624 m_pCommSocket = null;
625 }
626 this.ConnectionSpec = null;
627 this.DUZ = "";
628 m_bConnected=false;
629 }
630
631
632 public override string GetLoginFacility(String aDuz)
633 {
634 try
635 {
636 if (!this.IsConnected)
637 {
638 throw new BMXNetException("BMXNetBroker is not connected to RPMS");
639 }
640#if DEBUG
641 _watch = new Stopwatch();
642 _watch.Start();
643#endif
644 this.SendString(this.Socket, ADEBLDMsg(m_cHDR, "BMXGetFac", aDuz));
645 return this.ReceiveString(this.Socket);
646 }
647 catch (BMXNetException bmxEx)
648 {
649 throw new BMXNetException(bmxEx.Message + " || "+bmxEx.StackTrace);
650 }
651 catch (Exception ex)
652 {
653 throw ex;
654 }
655 }
656
657
658 protected String GetUserName()
659 {
660 return this.TransmitRPC("BMX USER", this.DUZ);
661 }
662
663 /// <summary>
664 /// Ping the server, will reset read-timeout
665 /// </summary>
666 /// <returns>Answer the #milliseconds to run or -1 if theres an issue</returns>
667 public double ImHereServer()
668 {
669 try
670 {
671 if (this.IsConnected)
672 {
673 DateTime past = DateTime.Now;
674 this.TransmitRPC("BMX IM HERE","");
675 return (DateTime.Now - past).TotalMilliseconds;
676 }
677 else
678 {
679 return -1;
680 }
681 }
682 catch
683 {
684 return -1;
685 }
686 }
687
688 #region RPX Properties
689
690
691
692 public bool Connected
693 {
694 get
695 {
696 return m_bConnected;
697 }
698 }
699
700
701 public string ServerAddress
702 {
703 get
704 {
705 return this.ConnectionSpec.Server;
706 }
707 }
708
709 public string ServerNamespace
710 {
711 get
712 {
713 return this.ConnectionSpec.NameSpace;
714 }
715 }
716
717 public int ServerPort
718 {
719 get
720 {
721 return this.ConnectionSpec.Port;
722 }
723 }
724
725
726 #endregion RPX Properties
727
728
729
730
731 }
732
733 /*
7341 ;;1;Signons not currently allowed on this processor.
7352 ;;1;Maximum number of users already signed on to this processor.
7363 ;;1;This device has not been defined to the system -- contact system manager.
7374 ;;0;Not a valid Windows Identity map value.
7385 ;;0;No Access Allowed for this User.
7396 ;;0;Invalid device password.
7407 ;;0;Device locked due to too many invalid sign-on attempts.
7418 ;;1;This device is out of service.
7429 ;;0;*** MULTIPLE SIGN-ONS NOT ALLOWED ***
74310 ;;1;You don't have access to this device!
74411 ;;0;Your access code has been terminated. Please see your site manager!
74512 ;;0;VERIFY CODE MUST be changed before continued use.
74613 ;;1;This device may only be used outside of this time frame |
74714 ;;0;'|' is not a valid UCI!
74815 ;;0;'|' is not a valid program name!
74916 ;;0;No PRIMARY MENU assigned to user or User is missing KEY to menu!
75017 ;;0;Your access to the system is prohibited from |.
75118 ;;0;Windows Integrated Security Not Allowed on this port.*/
752}
Note: See TracBrowser for help on using the repository browser.