Unhandled Exception on Disconnect

Mar 23, 2011 at 2:52 PM

Hi, been using your library for a few weeks and its working great!  However, I just got this exception (now 2nd time in a long time, so rare), but a program-crashing exception nonetheless. Seems to occur when my program is inside the sendUnixCommand function.  The important part looks like this:

try
{
    using (var client = new SshClient(connectionInfo)) 
    {
        client.Connect();
       
var cmd = client.RunCommand(command);
       
unixResults = cmd.Result;
        client.Disconnect();
    }

    return unixResults;
}
 

And here's the stack trace:

See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************
System.AggregateException: One or more errors occurred. ---> System.Net.Sockets.SocketException: An invalid argument was supplied
   at System.Net.Sockets.Socket.Shutdown(SocketShutdown how)
   at Renci.SshClient.Session.HandleMessage(DisconnectMessage message)
   at Renci.SshClient.Session.SendDisconnect(DisconnectReasons reasonCode, String message)
   at Renci.SshClient.Session.RaiseError(Exception exp)
   at Renci.SshClient.Session.MessageListener()
   at Renci.SshClient.Session.<Connect>b__8()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at Renci.SshClient.Session.Dispose(Boolean disposing)
   at Renci.SshClient.Session.Dispose()
   at Renci.SshClient.BaseClient.Dispose(Boolean disposing)
   at Renci.SshClient.BaseClient.Dispose()
   at RVLogViewer.MainForm.sendUnixCommand(String command)
   at RVLogViewer.MainForm.checkForFileInUnix()
   at RVLogViewer.MainForm.runQueryButton_Click(Object sender, EventArgs e)
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
---> (Inner Exception #0) System.Net.Sockets.SocketException (0x80004005): An invalid argument was supplied
   at System.Net.Sockets.Socket.Shutdown(SocketShutdown how)
   at Renci.SshClient.Session.HandleMessage(DisconnectMessage message)
   at Renci.SshClient.Session.SendDisconnect(DisconnectReasons reasonCode, String message)
   at Renci.SshClient.Session.RaiseError(Exception exp)
   at Renci.SshClient.Session.MessageListener()
   at Renci.SshClient.Session.<Connect>b__8()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()<---

 

************** Loaded Assemblies **************
mscorlib
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)
    CodeBase: file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll
----------------------------------------
RV Log Viewer
    Assembly Version: 1.0.0.0
    Win32 Version: 1.0.0.0
    CodeBase: file:///C:/Users/lhersman/AppData/Local/Apps/2.0/XGNED7CC.AAA/9LGGRTY6.LL7/rvlo..tion_cbf45051ea220fc7_0001.0000_34f4963113744e44/RV%20Log%20Viewer.exe
----------------------------------------
System.Windows.Forms
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System.Drawing
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
System
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Configuration
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Configuration/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll
----------------------------------------
System.Xml
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.dll
----------------------------------------
jlqpfid1
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
Accessibility
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Accessibility/v4.0_4.0.0.0__b03f5f7f11d50a3a/Accessibility.dll
----------------------------------------
Renci.SshClient
    Assembly Version: 0.1.0.0
    Win32 Version: 1.0.0.0
    CodeBase: file:///C:/Users/lhersman/AppData/Local/Apps/2.0/XGNED7CC.AAA/9LGGRTY6.LL7/rvlo..tion_cbf45051ea220fc7_0001.0000_34f4963113744e44/Renci.SshClient.DLL
----------------------------------------
System.Numerics
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Numerics/v4.0_4.0.0.0__b77a5c561934e089/System.Numerics.dll
----------------------------------------
System.Core
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1 built by: RTMRel
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Core/v4.0_4.0.0.0__b77a5c561934e089/System.Core.dll
----------------------------------------
Microsoft.CSharp
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Microsoft.CSharp/v4.0_4.0.0.0__b03f5f7f11d50a3a/Microsoft.CSharp.dll
----------------------------------------
System.Dynamic
    Assembly Version: 4.0.0.0
    Win32 Version: 4.0.30319.1
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Dynamic/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Dynamic.dll
----------------------------------------
Anonymously Hosted DynamicMethods Assembly
    Assembly Version: 0.0.0.0
    Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)
    CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_32/mscorlib/v4.0_4.0.0.0__b77a5c561934e089/mscorlib.dll
----------------------------------------

************** JIT Debugging **************
To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.

For example:

<configuration>
    <system.windows.forms jitDebugging="true" />
</configuration>

When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.

Coordinator
Mar 24, 2011 at 4:53 PM

Thanks for posting this information.

 

It looks like I am doing something wrong when Disposing an object so I will try to take a look at it soon and see if I can fix it.

 

Thanks,

Oleg

Mar 24, 2011 at 5:57 PM

Thanks for the reply Oleg, let me know how it turns out!

Apr 5, 2011 at 2:43 PM
Edited Apr 5, 2011 at 2:46 PM

I'm having the same issue.  

 

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in Renci.SshClient.dll

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in Renci.SshClient.dll

Apr 18, 2011 at 9:12 PM
Edited Apr 18, 2011 at 9:15 PM

A similar exception seems to be occuring a lot today:

See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************

System.AggregateException: One or more errors occurred. ---> System.ObjectDisposedException: Cannot access a disposed object.

Object name: 'System.Net.Sockets.Socket'.
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode)
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at Renci.SshClient.Session.Write(Byte[] data)
at Renci.SshClient.Session.SendMessage(Message message)
at Renci.SshClient.Session.SendDisconnect(DisconnectReasons reasonCode, String message)
at Renci.SshClient.Session.RaiseError(Exception exp)
at Renci.SshClient.Session.MessageListener()
at Renci.SshClient.Session.<Connect>b__8()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()

--- End of inner exception stack trace ---

at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Renci.SshClient.Session.Dispose(Boolean disposing)
at Renci.SshClient.Session.Dispose()
at Renci.SshClient.BaseClient.Dispose(Boolean disposing)
at Renci.SshClient.BaseClient.Dispose()
at RVLogViewer.MainForm.sendUnixCommand(String command)
at RVLogViewer.MainForm.checkForFileInUnix()
at RVLogViewer.MainForm.runQueryButton_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
---> (Inner Exception #0) System.ObjectDisposedException: Cannot access a disposed object.

Object name: 'System.Net.Sockets.Socket'.
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode)
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at Renci.SshClient.Session.Write(Byte[] data)
at Renci.SshClient.Session.SendMessage(Message message)
at Renci.SshClient.Session.SendDisconnect(DisconnectReasons reasonCode, String message)
at Renci.SshClient.Session.RaiseError(Exception exp)
at Renci.SshClient.Session.MessageListener()
at Renci.SshClient.Session.<Connect>b__8()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()<---

************** Loaded Assemblies **************

mscorlib
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)

CodeBase: file:///C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll

----------------------------------------

RV Log Viewer
Assembly Version: 1.0.0.0
Win32 Version: 1.0.0.0

CodeBase: file:///C:/Documents%20and%20Settings/shernandez/Local%20Settings/Apps/2.0/X6OEK7G1.K8R/JJQDTYZY.PNW/rvlo..tion_cbf45051ea220fc7_0001.0002_f1242c043af733c4/RV%20Log%20Viewer.exe

----------------------------------------

System.Windows.Forms
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll

----------------------------------------

System.Drawing
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll

----------------------------------------

System
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll

----------------------------------------

System.Configuration
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Configuration/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll

----------------------------------------

System.Xml
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Xml/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.dll

----------------------------------------

i5eg1gyc
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll

----------------------------------------

Renci.SshClient
Assembly Version: 0.1.0.0
Win32 Version: 1.0.0.0

CodeBase: file:///C:/Documents%20and%20Settings/shernandez/Local%20Settings/Apps/2.0/X6OEK7G1.K8R/JJQDTYZY.PNW/rvlo..tion_cbf45051ea220fc7_0001.0002_f1242c043af733c4/Renci.SshClient.DLL

----------------------------------------

System.Numerics
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Numerics/v4.0_4.0.0.0__b77a5c561934e089/System.Numerics.dll

----------------------------------------

System.Core
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Core/v4.0_4.0.0.0__b77a5c561934e089/System.Core.dll

----------------------------------------

Microsoft.CSharp
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/Microsoft.CSharp/v4.0_4.0.0.0__b03f5f7f11d50a3a/Microsoft.CSharp.dll

----------------------------------------

System.Dynamic
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Dynamic/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Dynamic.dll

----------------------------------------

Anonymously Hosted DynamicMethods Assembly
Assembly Version: 0.0.0.0
Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)

CodeBase: file:///C:/WINDOWS/Microsoft.Net/assembly/GAC_32/mscorlib/v4.0_4.0.0.0__b77a5c561934e089/mscorlib.dll

----------------------------------------

************** JIT Debugging **************

To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.

For example:
<configuration>
<system.windows.forms jitDebugging="true" />
</configuration>

When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.

Coordinator
Apr 18, 2011 at 11:39 PM

ok, it looks like what happens is that while I am waiting for a message back from he server, connection drops and socket is closed.

It looks like I need t check if socket is open before sending close message in case of exception,

 

I will try to look into into that either tonight or tomorrow.

 

Thanks,

Oleg

Apr 19, 2011 at 1:48 PM

Thanks man!  Also, since I catch the error the program dosn't crash, but it wont connect anymore after the exception until the program is re-started.

Coordinator
Apr 19, 2011 at 7:18 PM

Ok,

 

I tried to see what I am doing wrong and I have a guess, so I am checking this it.

I might be not cleaning up Socket object correctly so I did a small fix, which I hope can cover 50% of the problem.

For another one I need to ask you to put a breakpoint at this line 1452 of Session.cs file and to see the exact error it throws at this point.

 

What I suspect happens is that for some reason connection is closed by the server and I didnt catch yet so I think I need to catch appropriate exception at this point and handle it differently.

 

Thanks,

Oleg

Apr 20, 2011 at 12:16 AM

I'll try it tomorrow as well.

Apr 20, 2011 at 1:21 PM

It's not erroring at that point.  I just did a connect, then disconnect and it errors.  On line 817 it calls this._socket.Disconnect(true) and an error is thrown there. 

Says:

  System.ObjectDisposedException was unhandled by user code
  Message=Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
  Source=System
  ObjectName=System.Net.Sockets.Socket
  StackTrace:
       at System.Net.Sockets.Socket.Shutdown(SocketShutdown how)
       at Renci.SshClient.Session.HandleMessage(DisconnectMessage message) in C:\Projects\Utility\Renci.SshClient\Session.cs:line 815
       at Renci.SshClient.Session.SendDisconnect(DisconnectReasons reasonCode, String message) in C:\Projects\Utility\Renci.SshClient\Session.cs:line 793
       at Renci.SshClient.Session.RaiseError(Exception exp) in C:\Projects\Utility\Renci.SshClient\Session.cs:line 1563
       at Renci.SshClient.Session.MessageListener() in C:\Projects\Utility\Renci.SshClient\Session.cs:line 1533
       at Renci.SshClient.Session.<Connect>b__8() in C:\Projects\Utility\Renci.SshClient\Session.cs:line 490
       at System.Threading.Tasks.Task.InnerInvoke()
       at System.Threading.Tasks.Task.Execute()
  InnerException:

On line 815 of Session.cs, this._socket is null when it tries to call this._socket.Shutdown(SocketShutdown.Both); What's weird is, the line before it is: if (this._socket != null)

I'm not sure if this helps either.  As I was restarting the app over and over stepping through your code, the error occurred less and less. It's as if the more connection attempts I made, the less times it happened.  Yet when I would do a single run of the application in the past (including the first few runs this morning), it was crashing every time.  I'm confused now because I'm running it over and over, and switching between two different servers (to see if that made a diff) and now it won't crash.

 So I figured it'd put the connect/disconnect in a loop in 15 second intervals.  First time I ran it:

System.Net.Sockets.SocketException was caught
  ErrorCode=10053
  Message=An established connection was aborted by the software in your host machine
  NativeErrorCode=10053
  Source=Renci.SshClient
  StackTrace:
       at Renci.SshClient.Session.WaitHandle(WaitHandle waitHandle) in C:\Projects\Utility\Renci.SshClient\Session.cs:line 595
       at Renci.SshClient.Channels.Channel.Dispose(Boolean disposing) in C:\Projects\Utility\Renci.SshClient\Channels\Channel.cs:line 611
       at Renci.SshClient.Channels.ChannelSession.Dispose(Boolean disposing) in C:\Projects\Utility\Renci.SshClient\Channels\ChannelSession.cs:line 325
       at Renci.SshClient.Channels.Channel.Dispose() in C:\Projects\Utility\Renci.SshClient\Channels\Channel.cs:line 586
       at Renci.SshClient.Channels.Channel.Close() in C:\Projects\Utility\Renci.SshClient\Channels\Channel.cs:line 203
       at Renci.SshClient.Sftp.SftpSession.Dispose(Boolean disposing) in C:\Projects\Utility\Renci.SshClient\Sftp\SftpSession.cs:line 369
       at Renci.SshClient.Sftp.SftpSession.Dispose() in C:\Projects\Utility\Renci.SshClient\Sftp\SftpSession.cs:line 351
       at Renci.SshClient.Sftp.SftpSession.Disconnect() in C:\Projects\Utility\Renci.SshClient\Sftp\SftpSession.cs:line 98
       at Renci.SshClient.SftpClient.OnDisconnecting() in C:\Projects\Utility\Renci.SshClient\SftpClient.cs:line 421
       at Renci.SshClient.BaseClient.Disconnect() in C:\Projects\Utility\Renci.SshClient\BaseClient.cs:line 111
       at SFTP_Dialer_Loader.Module1.Run() in C:\Projects\Dialers\SFTP_Dialer_Loader\Module1.vb:line 97
  InnerException:

2nd time same thing, 3rd time it worked and started looping connects/disconnects.  Then I got forcibly closed by remote host, probably for my connection behavior.

Could there be an outside influence here?  Firewall maybe?  We run SEP in our office.  Maybe Network Threat Protection kicks in the first couple of attempts, but you'd think it would always block.  Plus, I'm one of our former Network/Sys Admins, so I'm not in any of the restricted policies. 

 

Coordinator
Apr 20, 2011 at 1:29 PM

Thanks for this research,

 

It gives me another idea, when it behaves strange like that it could be many things, but since you said it got to the line this._socket.Shutdown, even so I do check for null befor, it could be my synchronization issue,

which could explain why it happens sometimes, and other works ok.

Let me add some locking while disposing _socket and see if that could help.

 

Please use the latest code once I check it in to test it.

 

Thanks,

Oleg

Coordinator
May 11, 2011 at 4:03 AM

mrchen911,

Can you check latest source code cause

i just fixed very similar issue and whated to let you know so you could try it and let me know if it fixes your problem.

 

Thanks,

Oleg