Hochwertige Programmiersprachen sind beliebt, aber es gibt Bereiche, in denen Sie nicht verwaltete Bibliotheksimplementierungen verwenden müssen. Dies kann ein Aufruf bestimmter Betriebssystemfunktionen, ein Zugriff auf Geräte auf niedriger Ebene, die Notwendigkeit der Leistung von Algorithmen und andere sein. Unter dem Schnitt werde ich Ihnen sagen, was Sie auf Reisen in nicht verwaltetem Code antreffen können und was Sie mitnehmen sollten.
Sie stehen vor der Haustür Ihrer gemütlichen IDE und sind überhaupt nicht versucht, in die Welt des Quellcodes zu gehen, in der es dunkel und nichts klar ist. Für den Erfolg des Unternehmens müssen Sie zunächst eine Karte beschaffen - eine Beschreibung der Header der Bibliothek wird angezeigt, und es ist besser, eine vollständige Dokumentation zu haben. Es sieht normalerweise so aus:
...
#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);
...
Nehmen wir an, Sie haben Glück und die Dokumentation ist da. Es beschreibt die verwendeten Funktionssignaturen, Strukturen, Aliase und Verweise auf andere verwendete Header. Die erste Aufgabe besteht darin, die Bibliothek im Betriebssystem zu finden. Sein Name kann von den erwarteten abweichen:
~$ 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
Numerisches Suffix bedeutet verschiedene Versionen von Bibliotheken. Im Allgemeinen benötigen wir das Original libip4tc.so. Sie können mit einem Auge hineinschauen und sicherstellen, dass es sich lohnt:
~$ 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 . .
Damit ist unsere Reise beendet. Ich werde hinzufügen, dass die Verwendung von Bibliotheken auf niedriger Ebene ein schwieriger und unvorhersehbarer Ansatz für den Erfolg zu sein scheint. Mangelnde Dokumentation und komplexe Interaktionen können beängstigend sein. Manchmal ist dies jedoch der einzige Weg, um das gewünschte Ergebnis zu erzielen.