/// <summary>Calls a remote procedure on an ONC/RPC server.</summary> /// <remarks> /// Calls a remote procedure on an ONC/RPC server. /// <p>The <code>OncRpcUdpClient</code> uses a similar timeout scheme as /// the genuine Sun C implementation of ONC/RPC: it starts with a timeout /// of one second when waiting for a reply. If no reply is received within /// this time frame, the client doubles the timeout, sends a new request /// and then waits again for a reply. In every case the client will wait /// no longer than the total timeout set through the /// <see cref="OncRpcClient.setTimeout(int)">OncRpcClient.setTimeout(int)</see> /// method. /// </remarks> /// <param name="procedureNumber">Procedure number of the procedure to call.</param> /// <param name="versionNumber">Protocol version number.</param> /// <param name="params"> /// The parameters of the procedure to call, contained /// in an object which implements the /// <see cref="XdrAble">XdrAble</see> /// interface. /// </param> /// <param name="result">The object receiving the result of the procedure call.</param> /// <exception cref="OncRpcException">if an ONC/RPC error occurs.</exception> /// <exception cref="org.acplt.oncrpc.OncRpcException"></exception> public override void call(int procedureNumber, int versionNumber, XdrAble @params, XdrAble result) { lock (this) { //Refresh: for (int refreshesLeft = 1; refreshesLeft >= 0; --refreshesLeft) { bool refreshFlag = false; // // First, build the ONC/RPC call header. Then put the sending // stream into a known state and encode the parameters to be // sent. Finally tell the encoding stream to send all its data // to the server. Then wait for an answer, receive it and decode // it. So that's the bottom line of what we do right here. // nextXid(); // // We only create our request message once and reuse it in case // retransmission should be necessary -- with the exception being // credential refresh. In this case we need to create a new // request message. // OncRpcClientCallMessage callHeader = new OncRpcClientCallMessage (xid, program, versionNumber, procedureNumber, auth); OncRpcClientReplyMessage replyHeader = new OncRpcClientReplyMessage (auth); long stopTime = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) + timeout; int resendTimeout = retransmissionTimeout; do { // // Now enter the great loop where we send calls out to the server // and then sit there waiting for a reply. If none comes, we first // resend our call after one second, then two seconds, four seconds, // and so on, until we have reached the timeout for the call in total. // Note that this setting only applies if exponential back-off // retransmission has been selected. Per default we do not retransmit // any more, in order to be in line with the SUNRPC implementations. // try { // // Send call message to server. Remember that we've already // "connected" the datagram socket, so java.net knows whom // to send the datagram packets. // sendingXdr.beginEncoding(host, port); callHeader.xdrEncode(sendingXdr); @params.xdrEncode(sendingXdr); sendingXdr.endEncoding(); } catch (System.IO.IOException e) { throw (new OncRpcException(OncRpcException.RPC_CANTSEND , e.Message)); } // // Receive reply message from server -- at least try to do so... // In case of batched calls we don't need no stinkin' answer, so // we can do other, more interesting things. // if (timeout == 0) { return; } // // Wait for an answer to arrive... // for (; ;) { try { long currentTimeout = stopTime - (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond); if (currentTimeout > resendTimeout) { currentTimeout = resendTimeout; } else { if (currentTimeout < 1) { // // as setSoTimeout interprets a timeout of zero as // infinite (?§$@%&!!!) we need to ensure that we // have a finite timeout, albeit maybe an infinitesimal // finite one. currentTimeout = 1; } } socket.ReceiveTimeout = (int)currentTimeout; receivingXdr.beginDecoding(); // // Only accept incomming reply if it comes from the same // address we've sent the ONC/RPC call to. Otherwise throw // away the datagram packet containing the reply and start // over again, waiting for the next reply to arrive. // if (host.Equals(receivingXdr.getSenderAddress())) { // // First, pull off the reply message header of the // XDR stream. In case we also received a verifier // from the server and this verifier was invalid, broken // or tampered with, we will get an // OncRpcAuthenticationException right here, which will // propagate up to the caller. If the server reported // an authentication problem itself, then this will // be handled as any other rejected ONC/RPC call. // try { replyHeader.xdrDecode(receivingXdr); } catch (org.acplt.oncrpc.OncRpcException e) { // // ** SF bug #1262106 ** // // We ran into some sort of trouble. Usually this will have // been a buffer underflow. Whatever, end the decoding process // and ensure this way that the next call has a chance to start // from a clean state. // receivingXdr.endDecoding(); throw (e); } // // Only deserialize the result, if the reply matches the call // and if the reply signals a successful call. In case of an // unsuccessful call (which mathes our call nevertheless) throw // an exception. // if (replyHeader.messageId == callHeader.messageId) { if (!replyHeader.successfullyAccepted()) { receivingXdr.endDecoding(); // // Check whether there was an authentication // problem. In this case first try to refresh the // credentials. // if ((refreshesLeft > 0) && (replyHeader.replyStatus == org.acplt.oncrpc.OncRpcReplyStatus .ONCRPC_MSG_DENIED) && (replyHeader.rejectStatus == org.acplt.oncrpc.OncRpcRejectStatus .ONCRPC_AUTH_ERROR) && (auth != null) && auth.canRefreshCred()) { // // Think about using a TAB size of four ;) // // Another instance of "CONTINUE considered // useful"... // refreshFlag = true; break; } // continue Refresh; // // Nope. No chance. This gets tough. // throw (replyHeader.newException()); } // // The reply header is okay and the call had been // accepted by the ONC/RPC server, so we can now // proceed to decode the outcome of the RPC. // try { result.xdrDecode(receivingXdr); } catch (OncRpcException e) { // // ** SF bug #1262106 ** // // We ran into some sort of trouble. Usually this will have // been a buffer underflow. Whatever, end the decoding process // and ensure this way that the next call has a chance to start // from a clean state. // receivingXdr.endDecoding(); throw (e); } // // Free pending resources of buffer and exit the call loop, // returning the reply to the caller through the result // object. // receivingXdr.endDecoding(); return; } } } catch (SocketException) { // // The message id did no match -- probably some // old UDP datagram which just popped up from the // middle of the Internet. // // Yet another case of "CONTINUE considered not // harmful"... // // [Nothing to do here, just wait for the next datagram] // // IP address of received UDP datagram is not the same // as the IP address of the ONC/RPC server. // // [Nothing to do here, just wait for the next datagram] // // Note that we only catch timeouts here, but no other // exceptions. Those others will go up further until someone // catches them. The timeouts are caught, so they can do no // damage but instead we start another round of sending a // request and waiting for a reply. Reminds me of NASA and // their "Mars Polar Lander"... // // Note that we need to leave the inner waiting loop here, // as we might need to resend the (lost) RPC request // datagram. // //Console.Out.WriteLine("This is supposed to be ignored"); //Console.Out.WriteLine(e.Message); //Console.Out.WriteLine(e.StackTrace); break; } catch (IOException e) { // // Argh. Trouble with the transport. Seems like we can't // receive data. Gosh. Go away! // try { receivingXdr.endDecoding(); } catch (IOException) { } // skip UDP record throw (new OncRpcException(OncRpcException.RPC_CANTRECV , e.Message)); } catch (org.acplt.oncrpc.OncRpcException e) { // // Ooops. An ONC/RPC exception. Let us rethrow this one, // as we won't have nothin' to do with it... // try { receivingXdr.endDecoding(); } catch (System.IO.IOException) { } // skip UDP record // // Well, in case we got not a *reply* RPC message back, // we keep listening for messages. // if (e.getReason() != org.acplt.oncrpc.OncRpcException.RPC_WRONGMESSAGE) { throw (e); } } // // We can not make use of the reply we just received, so // we need to dump it. // // This should raise no exceptions, when skipping the UDP // record. So if one is raised, we will rethrow an ONC/RPC // exception instead. // try { receivingXdr.endDecoding(); } catch (System.IO.IOException e) { throw (new OncRpcException(OncRpcException.RPC_CANTRECV , e.Message)); } } // jmw 12/18/2009 fix for refresh/continue in Java code if (refreshFlag) { break; } // // We only reach this code part beyond the inner waiting // loop if we run in a timeout and might need to retransmit // // According to the retransmission strategy choosen, update the // current retransmission (resending) timeout. // if (retransmissionMode == org.acplt.oncrpc.OncRpcUdpRetransmissionMode.EXPONENTIAL) { resendTimeout *= 2; } }while (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond < stopTime); // jmw 12/18/2009 don't want to throw an exception here if (refreshFlag) { continue; } // // That's it -- this shitty server does not talk to us. Now, due to // the indecent language used in the previous sentence, this software // can not be exported any longer to some countries of the world. // But this is surely not my problem, but rather theirs. So go away // and hide yourself in the dark with all your zombies (or maybe // kangaroos). // throw (new OncRpcTimeoutException()); } } }
/// <summary>Calls a remote procedure on an ONC/RPC server.</summary> /// <remarks> /// Calls a remote procedure on an ONC/RPC server. /// <p>The <code>OncRpcUdpClient</code> uses a similar timeout scheme as /// the genuine Sun C implementation of ONC/RPC: it starts with a timeout /// of one second when waiting for a reply. If no reply is received within /// this time frame, the client doubles the timeout, sends a new request /// and then waits again for a reply. In every case the client will wait /// no longer than the total timeout set through the /// <see cref="OncRpcClient.setTimeout(int)">OncRpcClient.setTimeout(int)</see> /// method. /// </remarks> /// <param name="procedureNumber">Procedure number of the procedure to call.</param> /// <param name="versionNumber">Protocol version number.</param> /// <param name="params"> /// The parameters of the procedure to call, contained /// in an object which implements the /// <see cref="XdrAble">XdrAble</see> /// interface. /// </param> /// <param name="result">The object receiving the result of the procedure call.</param> /// <exception cref="OncRpcException">if an ONC/RPC error occurs.</exception> /// <exception cref="org.acplt.oncrpc.OncRpcException"></exception> public override void call(int procedureNumber, int versionNumber, XdrAble @params, XdrAble result) { lock (this) { //Refresh: for (int refreshesLeft = 1; refreshesLeft >= 0; --refreshesLeft) { bool refreshFlag = false; // // First, build the ONC/RPC call header. Then put the sending // stream into a known state and encode the parameters to be // sent. Finally tell the encoding stream to send all its data // to the server. Then wait for an answer, receive it and decode // it. So that's the bottom line of what we do right here. // nextXid(); // // We only create our request message once and reuse it in case // retransmission should be necessary -- with the exception being // credential refresh. In this case we need to create a new // request message. // OncRpcClientCallMessage callHeader = new OncRpcClientCallMessage (xid, program, versionNumber, procedureNumber, auth); OncRpcClientReplyMessage replyHeader = new OncRpcClientReplyMessage (auth); long stopTime = (DateTime.Now.Ticks/TimeSpan.TicksPerMillisecond) + timeout; int resendTimeout = retransmissionTimeout; do { // // Now enter the great loop where we send calls out to the server // and then sit there waiting for a reply. If none comes, we first // resend our call after one second, then two seconds, four seconds, // and so on, until we have reached the timeout for the call in total. // Note that this setting only applies if exponential back-off // retransmission has been selected. Per default we do not retransmit // any more, in order to be in line with the SUNRPC implementations. // try { // // Send call message to server. Remember that we've already // "connected" the datagram socket, so java.net knows whom // to send the datagram packets. // sendingXdr.beginEncoding(host, port); callHeader.xdrEncode(sendingXdr); @params.xdrEncode(sendingXdr); sendingXdr.endEncoding(); } catch (System.IO.IOException e) { throw (new OncRpcException(OncRpcException.RPC_CANTSEND , e.Message)); } // // Receive reply message from server -- at least try to do so... // In case of batched calls we don't need no stinkin' answer, so // we can do other, more interesting things. // if (timeout == 0) { return; } // // Wait for an answer to arrive... // for (; ; ) { try { long currentTimeout = stopTime - (DateTime.Now.Ticks/TimeSpan.TicksPerMillisecond); if (currentTimeout > resendTimeout) { currentTimeout = resendTimeout; } else { if (currentTimeout < 1) { // // as setSoTimeout interprets a timeout of zero as // infinite (?§$@%&!!!) we need to ensure that we // have a finite timeout, albeit maybe an infinitesimal // finite one. currentTimeout = 1; } } socket.ReceiveTimeout = (int) currentTimeout; receivingXdr.beginDecoding(); // // Only accept incomming reply if it comes from the same // address we've sent the ONC/RPC call to. Otherwise throw // away the datagram packet containing the reply and start // over again, waiting for the next reply to arrive. // if (host.Equals(receivingXdr.getSenderAddress())) { // // First, pull off the reply message header of the // XDR stream. In case we also received a verifier // from the server and this verifier was invalid, broken // or tampered with, we will get an // OncRpcAuthenticationException right here, which will // propagate up to the caller. If the server reported // an authentication problem itself, then this will // be handled as any other rejected ONC/RPC call. // try { replyHeader.xdrDecode(receivingXdr); } catch (org.acplt.oncrpc.OncRpcException e) { // // ** SF bug #1262106 ** // // We ran into some sort of trouble. Usually this will have // been a buffer underflow. Whatever, end the decoding process // and ensure this way that the next call has a chance to start // from a clean state. // receivingXdr.endDecoding(); throw (e); } // // Only deserialize the result, if the reply matches the call // and if the reply signals a successful call. In case of an // unsuccessful call (which mathes our call nevertheless) throw // an exception. // if (replyHeader.messageId == callHeader.messageId) { if (!replyHeader.successfullyAccepted()) { receivingXdr.endDecoding(); // // Check whether there was an authentication // problem. In this case first try to refresh the // credentials. // if ((refreshesLeft > 0) && (replyHeader.replyStatus == org.acplt.oncrpc.OncRpcReplyStatus .ONCRPC_MSG_DENIED) && (replyHeader.rejectStatus == org.acplt.oncrpc.OncRpcRejectStatus .ONCRPC_AUTH_ERROR) && (auth != null) && auth.canRefreshCred()) { // // Think about using a TAB size of four ;) // // Another instance of "CONTINUE considered // useful"... // refreshFlag = true; break; } // continue Refresh; // // Nope. No chance. This gets tough. // throw (replyHeader.newException()); } // // The reply header is okay and the call had been // accepted by the ONC/RPC server, so we can now // proceed to decode the outcome of the RPC. // try { result.xdrDecode(receivingXdr); } catch (OncRpcException e) { // // ** SF bug #1262106 ** // // We ran into some sort of trouble. Usually this will have // been a buffer underflow. Whatever, end the decoding process // and ensure this way that the next call has a chance to start // from a clean state. // receivingXdr.endDecoding(); throw (e); } // // Free pending resources of buffer and exit the call loop, // returning the reply to the caller through the result // object. // receivingXdr.endDecoding(); return; } } } catch (SocketException) { // // The message id did no match -- probably some // old UDP datagram which just popped up from the // middle of the Internet. // // Yet another case of "CONTINUE considered not // harmful"... // // [Nothing to do here, just wait for the next datagram] // // IP address of received UDP datagram is not the same // as the IP address of the ONC/RPC server. // // [Nothing to do here, just wait for the next datagram] // // Note that we only catch timeouts here, but no other // exceptions. Those others will go up further until someone // catches them. The timeouts are caught, so they can do no // damage but instead we start another round of sending a // request and waiting for a reply. Reminds me of NASA and // their "Mars Polar Lander"... // // Note that we need to leave the inner waiting loop here, // as we might need to resend the (lost) RPC request // datagram. // //Console.Out.WriteLine("This is supposed to be ignored"); //Console.Out.WriteLine(e.Message); //Console.Out.WriteLine(e.StackTrace); break; } catch (IOException e) { // // Argh. Trouble with the transport. Seems like we can't // receive data. Gosh. Go away! // try { receivingXdr.endDecoding(); } catch (IOException) { } // skip UDP record throw (new OncRpcException(OncRpcException.RPC_CANTRECV , e.Message)); } catch (org.acplt.oncrpc.OncRpcException e) { // // Ooops. An ONC/RPC exception. Let us rethrow this one, // as we won't have nothin' to do with it... // try { receivingXdr.endDecoding(); } catch (System.IO.IOException) { } // skip UDP record // // Well, in case we got not a *reply* RPC message back, // we keep listening for messages. // if (e.getReason() != org.acplt.oncrpc.OncRpcException.RPC_WRONGMESSAGE) { throw (e); } } // // We can not make use of the reply we just received, so // we need to dump it. // // This should raise no exceptions, when skipping the UDP // record. So if one is raised, we will rethrow an ONC/RPC // exception instead. // try { receivingXdr.endDecoding(); } catch (System.IO.IOException e) { throw (new OncRpcException(OncRpcException.RPC_CANTRECV , e.Message)); } } // jmw 12/18/2009 fix for refresh/continue in Java code if (refreshFlag) { break; } // // We only reach this code part beyond the inner waiting // loop if we run in a timeout and might need to retransmit // // According to the retransmission strategy choosen, update the // current retransmission (resending) timeout. // if (retransmissionMode == org.acplt.oncrpc.OncRpcUdpRetransmissionMode.EXPONENTIAL) { resendTimeout *= 2; } } while (DateTime.Now.Ticks/TimeSpan.TicksPerMillisecond < stopTime); // jmw 12/18/2009 don't want to throw an exception here if (refreshFlag) { continue; } // // That's it -- this shitty server does not talk to us. Now, due to // the indecent language used in the previous sentence, this software // can not be exported any longer to some countries of the world. // But this is surely not my problem, but rather theirs. So go away // and hide yourself in the dark with all your zombies (or maybe // kangaroos). // throw (new OncRpcTimeoutException()); } } }