1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Linq;
9 using System.Collections.Generic;
10 using System.Net;
11 using System.Net.Sockets;
12 using Antmicro.Migrant;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Exceptions;
16 
17 namespace Antmicro.Renode.Sockets
18 {
19     public class SocketsManager : IDisposable
20     {
SocketsManager()21         static SocketsManager()
22         {
23             Instance = new SocketsManager();
24             sockets = new List<SocketInstance>();
25         }
26 
Dispose()27         public void Dispose()
28         {
29             foreach(var socketInstance in sockets)
30             {
31                 socketInstance.Dispose();
32             }
33         }
34 
CleanUp()35         public void CleanUp()
36         {
37             sockets = new List<SocketInstance>();
38         }
39 
List()40         public string[,] List()
41         {
42             var table = new Table().AddRow("Owner", "Type", "EndPoint", "Bound", "Connected");
43             table.AddRows(sockets, x => x.Owner.ToString(), x => x.Type.ToString(), x => x.EndPoint, x => x.IsBound.ToString(), x => x.IsConnected.ToString());
44             return table.ToArray();
45         }
46 
AcquireSocket(IEmulationElement owner, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, EndPoint endpoint, string nameAppendix = R, int? listeningBacklog = null, int connectingTimeout = 0, int receiveTimeout = 0, int sendTimeout = 0, bool asClient = false, bool noDelay = true)47         public Socket AcquireSocket(IEmulationElement owner, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, EndPoint endpoint, string nameAppendix = "", int? listeningBacklog = null, int connectingTimeout = 0, int receiveTimeout = 0, int sendTimeout = 0, bool asClient = false, bool noDelay = true)
48         {
49             var s = new SocketInstance(owner, addressFamily, socketType, protocolType, endpoint, nameAppendix: nameAppendix, asClient: asClient, noDelay: noDelay, listeningBacklog: listeningBacklog);
50             sockets.Add(s);
51             return s.socket;
52         }
53 
TryDropSocket(Socket socket)54         public bool TryDropSocket(Socket socket)
55         {
56             SocketInstance socketInstance = sockets.Where(x => x.socket == socket).FirstOrDefault();
57             if(socketInstance != null)
58             {
59                 sockets.Remove(socketInstance);
60                 socketInstance.Dispose();
61                 return true;
62             }
63             return false;
64         }
65 
66         [Transient]
67         private static List<SocketInstance> sockets;
68 
69         public static SocketsManager Instance { get; private set; }
70 
71         class SocketInstance : IDisposable
72         {
SocketInstance(IEmulationElement owner, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, EndPoint endpoint, string nameAppendix = R, int? listeningBacklog = null, bool asClient = false, bool noDelay = true)73             public SocketInstance(IEmulationElement owner, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, EndPoint endpoint, string nameAppendix = "", int? listeningBacklog = null, bool asClient = false, bool noDelay = true)
74             {
75                 string name;
76                 if(!EmulationManager.Instance.CurrentEmulation.TryGetEmulationElementName((object)owner, out name))
77                 {
78                     name = owner?.ToString() ?? ""; // If name is not obtainable, use the peripheral name
79                 }
80                 ownerName = String.Format("{0}{1}{2}", name, name.Length == 0 ? "" : ":", nameAppendix);
81                 socket = new Socket(addressFamily, socketType, protocolType);
82                 if(protocolType == ProtocolType.Tcp)
83                 {
84                     socket.NoDelay = noDelay;
85                 }
86                 this.endpoint = endpoint.ToString();
87                 try
88                 {
89                     if(asClient)
90                     {
91                         socket.Connect(endpoint);
92                     }
93                     else
94                     {
95                         socket.Bind(endpoint);
96                         socket.Listen(listeningBacklog ?? 0);
97                     }
98                 }
99                 catch(SocketException e)
100                 {
101                     throw new RecoverableException($"Unable to create '{this.endpoint}' socket:[{e.SocketErrorCode}] {e.Message}");
102                 }
103             }
104 
Dispose()105             public void Dispose()
106             {
107                 try
108                 {
109                     if(socket.Connected)
110                     {
111                         socket.Shutdown(SocketShutdown.Both);
112                     }
113                 }
114                 finally
115                 {
116                     socket.Close();
117                 }
118 
119                 socket.Dispose();
120             }
121 
122             public string EndPoint => endpoint;
123             public bool IsConnected => socket.Connected;
124             public bool IsBound => socket.IsBound;
125             public SocketType Type => socket.SocketType;
126             public string Owner => ownerName;
127 
128             private string ownerName;
129             private string endpoint;
130             public Socket socket;
131         }
132     }
133 }
134