diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index a4921b845..789b835a5 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -39,7 +39,7 @@
 #include "Constants.hpp"
 #include "Utils.hpp"
 
-#ifdef __GNUC__
+#if defined(__GNUC__) && (!defined(ZT_NO_TYPE_PUNNING))
 #define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
 #else
 #define ZT_VAR_MAY_ALIAS
@@ -204,15 +204,19 @@ public:
 	{
 		if ((i + sizeof(T)) > _l)
 			throw std::out_of_range("Buffer: setAt() beyond end of data");
+#ifdef ZT_NO_TYPE_PUNNING
+		uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
+		for(unsigned int x=1;x<=sizeof(T);++x)
+			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
+#else
 		T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + i);
 		*p = Utils::hton(v);
+#endif
 	}
 
 	/**
 	 * Get a primitive integer value at a given position
 	 * 
-	 * This behaves like set() in reverse.
-	 * 
 	 * @param i Index to get integer
 	 * @tparam T Integer type (e.g. uint16_t, int64_t)
 	 * @return Integer value
@@ -223,8 +227,18 @@ public:
 	{
 		if ((i + sizeof(T)) > _l)
 			throw std::out_of_range("Buffer: at() beyond end of data");
+#ifdef ZT_NO_TYPE_PUNNING
+		T v = 0;
+		const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
+		for(unsigned int x=0;x<sizeof(T);++x) {
+			v <<= 8;
+			v |= (T)*(p++);
+		}
+		return v;
+#else
 		const T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T *>(_b + i);
 		return Utils::ntoh(*p);
+#endif
 	}
 
 	/**
@@ -240,8 +254,14 @@ public:
 	{
 		if ((_l + sizeof(T)) > C)
 			throw std::out_of_range("Buffer: append beyond capacity");
+#ifdef ZT_NO_TYPE_PUNNING
+		uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
+		for(unsigned int x=1;x<=sizeof(T);++x)
+			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
+#else
 		T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + _l);
 		*p = Utils::hton(v);
+#endif
 		_l += sizeof(T);
 	}
 
diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp
index ae8e18023..de8f1569f 100644
--- a/node/Salsa20.cpp
+++ b/node/Salsa20.cpp
@@ -19,13 +19,15 @@
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 
-// Slow version that does not use type punning
-//#define U8TO32_LITTLE(p) ( ((uint32_t)(p)[0]) | ((uint32_t)(p)[1] << 8) | ((uint32_t)(p)[2] << 16) | ((uint32_t)(p)[3] << 24) )
-//static inline void U32TO8_LITTLE(uint8_t *const c,const uint32_t v) { c[0] = (uint8_t)v; c[1] = (uint8_t)(v >> 8); c[2] = (uint8_t)(v >> 16); c[3] = (uint8_t)(v >> 24); }
-
+#ifdef ZT_NO_TYPE_PUNNING
+// Slower version that does not use type punning
+#define U8TO32_LITTLE(p) ( ((uint32_t)(p)[0]) | ((uint32_t)(p)[1] << 8) | ((uint32_t)(p)[2] << 16) | ((uint32_t)(p)[3] << 24) )
+static inline void U32TO8_LITTLE(uint8_t *const c,const uint32_t v) { c[0] = (uint8_t)v; c[1] = (uint8_t)(v >> 8); c[2] = (uint8_t)(v >> 16); c[3] = (uint8_t)(v >> 24); }
+#else
 // Fast version that just does 32-bit load/store
 #define U8TO32_LITTLE(p) (*((const uint32_t *)((const void *)(p))))
 #define U32TO8_LITTLE(c,v) *((uint32_t *)((void *)(c))) = (v)
+#endif // ZT_NO_TYPE_PUNNING
 
 #else // __BYTE_ORDER == __BIG_ENDIAN (we don't support anything else... does MIDDLE_ENDIAN even still exist?)