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

Last change on this file since 1189 was 1180, checked in by Sam Habiel, 14 years ago

RemoteSession.cs Interface: Added:

  • Lock method to lock glvns on M db.
  • Encoding ConnectionEncoding to set the connection encoding on the DB

BMXNetSessionConnection: Added:

  • Abstract ConnectionEncoding property
  • Clarified error message that gets called. It now says that you don't have TransmitRPC writer lock??? When the error could be any BMXNetException.

BMXNetSessionSocketConnection:

  • Added ConnectionEncoding property
  • ReceiveString completely refactored: now we get much better performance
  • Timers and Debug Writes are all over the place now.

BMXNetRemoteSession:

  • Implemented the 2 new 'stuff' in Interface RemoteSesssion: -- Lock glvn -- Encoding Property
  • TableFromSQL with Dataset has an honest to god bug in it: The passed dataset and table name are not used even though they are passed.

BMXNetSessionConnectionOverAnotherSessionConnection:

  • Implemented the Encoding Property in Interface RemoteSession to have the project compile.

Updated dlls. Have fun.

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