Linguagens de programação de alto nível são populares, mas há áreas em que você precisará usar implementações de biblioteca não gerenciadas. Pode ser uma chamada para funções específicas do sistema operacional, acesso de baixo nível a dispositivos, necessidade de desempenho em algoritmos e outros. Abaixo do corte, direi o que você pode encontrar ao viajar em código não gerenciado e o que você deve levar com você.
Você está parado na porta de seu IDE aconchegante e não se sente tentado a ir para o mundo do código-fonte, onde está escuro e nada é claro. Para o sucesso do empreendimento, em primeiro lugar, você precisa de um mapa - uma descrição dos cabeçalhos da biblioteca aparecerá, e é melhor ter a documentação completa. Geralmente é assim:
...
#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 você esteja com sorte e a documentação esteja lá. Ele descreve as assinaturas de função, estruturas usadas, aliases e referências a outros cabeçalhos usados. A primeira missão é encontrar a biblioteca no sistema operacional. Seu nome pode ser diferente do 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
Sufixo numérico significa diferentes versões de bibliotecas. Em geral, precisamos do libip4tc.so original. Você pode olhar para dentro com um olho e certificar-se de que vale a 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);
}
/* 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
. :
.
.
, , . 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];
};
:
#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 . .
Isso conclui nossa jornada. Acrescentarei que usar bibliotecas de baixo nível parece ser uma abordagem difícil e imprevisível em termos de sucesso. A falta de documentação e interações complexas podem ser assustadoras. No entanto, às vezes, essa é a única maneira de alcançar o resultado desejado.