Viajar en código no administrado: ida y vuelta

Los lenguajes de programación de alto nivel son populares, pero hay áreas en las que necesitará utilizar implementaciones de bibliotecas no administradas. Esto puede ser una llamada a funciones específicas del sistema operativo, acceso de bajo nivel a dispositivos, la necesidad de rendimiento en algoritmos y otros. Debajo del corte, le diré lo que puede encontrar mientras viaja en código no administrado y lo que debe llevar consigo.





Estás parado en la puerta de tu acogedor IDE, y no tienes la tentación de ir al mundo del código fuente, donde está oscuro y nada claro. Para el éxito de la empresa, en primer lugar, debe obtener un mapa: aparecerá una descripción de los encabezados de la biblioteca y es mejor tener la documentación completa. Suele tener este aspecto:





...
#include <linux/netfilter_ipv4/ip_tables.h>
#include <libiptc/xtcshared.h>

#ifdef __cplusplus
extern "C" {
#endif

#define iptc_handle xtc_handle
#define ipt_chainlabel xt_chainlabel

#define IPTC_LABEL_ACCEPT  "ACCEPT"
#define IPTC_LABEL_DROP    "DROP"
#define IPTC_LABEL_QUEUE   "QUEUE"
#define IPTC_LABEL_RETURN  "RETURN"

/* Does this chain exist? */
int iptc_is_chain(const char *chain, struct xtc_handle *const handle);

/* Take a snapshot of the rules.  Returns NULL on error. */
struct xtc_handle *iptc_init(const char *tablename);

/* Cleanup after iptc_init(). */
void iptc_free(struct xtc_handle *h);
...
      
      



Digamos que tiene suerte y la documentación está ahí. Describe las firmas de funciones, las estructuras utilizadas, los alias y las referencias a otros encabezados utilizados. La primera misión es encontrar la biblioteca en el sistema operativo. Su nombre puede diferir de lo esperado:





~$ find /usr/lib/x86_64-linux-gnu/ -maxdepth 1 -name 'libip*'
/usr/lib/x86_64-linux-gnu/libip6tc.so.0.1.0
/usr/lib/x86_64-linux-gnu/libip4tc.so
/usr/lib/x86_64-linux-gnu/libiptc.so.0
/usr/lib/x86_64-linux-gnu/libip4tc.so.0.1.0
/usr/lib/x86_64-linux-gnu/libip6tc.so.0
/usr/lib/x86_64-linux-gnu/libiptc.so.0.0.0
/usr/lib/x86_64-linux-gnu/libip4tc.so.0
/usr/lib/x86_64-linux-gnu/libiptc.so
/usr/lib/x86_64-linux-gnu/libip6tc.so
      
      



Sufijo numérico significa diferentes versiones de bibliotecas. En general, necesitamos el libip4tc.so original. Puedes mirar adentro con un ojo y asegurarte de que vale la pena:





~$ nm -D /usr/lib/x86_64-linux-gnu/libip4tc.so
...
0000000000206230 D _edata
0000000000206240 B _end
                 U __errno_location
                 U fcntl
000000000000464c T _fini
                 U __fprintf_chk
                 U free
                 U getsockopt
                 w __gmon_start__
0000000000001440 T _init
0000000000003c80 T iptc_append_entry
0000000000003700 T iptc_builtin
0000000000004640 T iptc_check_entry
0000000000003100 T iptc_commit
0000000000002ff0 T iptc_create_chain
00000000000043f0 T iptc_delete_chain
...
      
      



,   , , . :





public static class Libiptc4
{
        /* Prototype: iptc_handle_t iptc_init(const char *tablename) */
        [DllImport("libip4tc.so")]
        public static extern IntPtr iptc_init(string tablename);
} 
      
      



: . . IntPtr



. , :





/* Prototype: iptc_handle_t iptc_init(const char *tablename) */
[DllImport("libip4tc.so")]
public static extern IntPtr iptc_init(IntPtr tblPtr);
...
var tblPtr = Marshal.StringToHGlobalAnsi("filter");
var _handle = Libiptc4.iptc_init_ptr(tblPtr);
Marshal.FreeHGlobal(tblPtr);
      
      



.



, , , , .





, : . :





struct ipt_entry {
	struct ipt_ip ip;

	/* Mark with fields that we care about. */
	unsigned int nfcache;

	/* Size of ipt_entry + matches */
	__u16 target_offset;
	/* Size of ipt_entry + matches + target */
	__u16 next_offset;

	/* Back pointer */
	unsigned int comefrom;

	/* Packet and byte counters. */
	struct xt_counters counters;

	/* The matches (if any), then the target. */
	unsigned char elems[0];
};
      
      



unsigned char elems[0]



. , , . :





*******************************************
* ip_entry                                *
* 112 bytes                               *
*******************************************
* matches                                 *
* target_offset - 112 bytes               *
*******************************************
* target                                  *
* next_offset - target_offset - 112 bytes *
*******************************************
      
      



(matches



target



) ip_entry



. :





  1. .





  2. .





, , . ipt_entry



:





[StructLayout(LayoutKind.Sequential)]
public struct IptEntry
{
 	public IptIp ip;
	public uint nfcache;
	public ushort target_offset;
	public ushort next_offset;
	public uint comefrom;
	public IptCounters counters;
};
      
      



Marshal.SizeOf<IptEntry>()



112 . matches



target



( ). : libiptc



8 ( long



), . , . :





static readonly int  _WORDLEN = Marshal.SizeOf<long>();
public static int Align(int size)
{
	return ((size + (_WORDLEN - 1)) & ~(_WORDLEN - 1));
}
      
      



, entry.target_offset



entry.next_offset



, :





IntPtr entryPtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr<IptEntry>(entryPtr, entry, false);
Marshal.StructureToPtr<Match>(entryPtr + 112, match, false);
      
      



:  , , :





var entry = Marshal.PtrToStructure<IptEntry>(point);
var match = Marshal.PtrToStructure<Match>(point + 112)
      
      



, union:







struct xt_entry_match {
	union {
		struct {
			__u16 match_size;

			/* Used by userspace */
			char name[XT_EXTENSION_MAXNAMELEN];
			__u8 revision;
		} user;
		struct {
			__u16 match_size;

			/* Used inside the kernel */
			struct xt_match *match;
		} kernel;

		/* Total length */
		__u16 match_size;
	} u;

	unsigned char data[0];
};
      
      



Union - : . c#. . , ( ). .





:





#define XT_EXTENSION_MAXNAMELEN   29
...
char name [XT_EXTENSION_MAXNAMELEN]
      
      



. header .





, . ushort, uint long , . . : , . . . :





byte [] convArray = BitConverter.GetBytes(value);
Array.Reverse(convArray);
ushort reverseEndian = BitConverter.ToUInt16(convArray,0);
      
      



ushort reverseEndian = (ushort)((value << 8) | (value >> 8));
      
      



. unmanaged code . /, errno. . , :





[DllImport("libip4tc.so", SetLastError = true)]
      
      



, , :





int errno = Marshal.GetLastWin32Error();
var errPtr = Libiptc4.iptc_strerror(errno);
string errStr = Marshal.PtrToStringAnsi(errPtr);
      
      



Linux c net.core (, /). : -, 32/64 , Windows . .





Con esto concluye nuestro viaje. Agregaré que el uso de bibliotecas de bajo nivel parece ser un enfoque difícil e impredecible en términos de éxito. La falta de documentación y las interacciones complejas pueden ser aterradoras. Sin embargo, a veces esta es la única forma de lograr el resultado deseado.








All Articles